1 /*
2  * Copyright 2014-2016 James Geboski <jgeboski@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <string.h>
19 
20 #include "facebook-api.h"
21 #include "facebook-data.h"
22 
23 struct _FbDataPrivate
24 {
25     FbApi *api;
26     struct im_connection *ic;
27     GQueue *msgs;
28     GQueue *tids;
29     GHashTable *evs;
30     GHashTable *gcs;
31 };
32 
33 static const gchar *fb_props_strs[] = {
34     "cid",
35     "did",
36     "stoken",
37     "token"
38 };
39 
40 G_DEFINE_TYPE_WITH_PRIVATE(FbData, fb_data, G_TYPE_OBJECT);
41 
42 static void
fb_data_dispose(GObject * obj)43 fb_data_dispose(GObject *obj)
44 {
45     FbDataPrivate *priv = FB_DATA(obj)->priv;
46     GHashTableIter iter;
47     gpointer ptr;
48 
49     g_object_unref(priv->api);
50     g_hash_table_iter_init(&iter, priv->evs);
51 
52     while (g_hash_table_iter_next(&iter, NULL, &ptr)) {
53         g_hash_table_iter_remove(&iter);
54         b_event_remove(GPOINTER_TO_UINT(ptr));
55     }
56 
57     g_hash_table_iter_init(&iter, priv->gcs);
58 
59     while (g_hash_table_iter_next(&iter, NULL, &ptr)) {
60         g_hash_table_iter_remove(&iter);
61         imcb_chat_free(ptr);
62     }
63 
64     g_queue_free_full(priv->msgs, (GDestroyNotify) fb_api_message_free);
65     g_queue_free_full(priv->tids, g_free);
66 
67     g_hash_table_destroy(priv->evs);
68     g_hash_table_destroy(priv->gcs);
69 }
70 
71 static void
fb_data_class_init(FbDataClass * klass)72 fb_data_class_init(FbDataClass *klass)
73 {
74     GObjectClass *gklass = G_OBJECT_CLASS(klass);
75 
76     gklass->dispose = fb_data_dispose;
77 }
78 
79 static void
fb_data_init(FbData * fata)80 fb_data_init(FbData *fata)
81 {
82     FbDataPrivate *priv;
83 
84     priv = fb_data_get_instance_private(fata);
85     fata->priv = priv;
86 
87     priv->api = fb_api_new();
88     priv->msgs = g_queue_new();
89     priv->tids = g_queue_new();
90     priv->evs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
91     priv->gcs = g_hash_table_new(g_direct_hash, g_direct_equal);
92 }
93 
94 FbData *
fb_data_new(account_t * acct)95 fb_data_new(account_t *acct)
96 {
97     FbData *fata;
98     FbDataPrivate *priv;
99 
100     fata = g_object_new(FB_TYPE_DATA, NULL);
101     priv = fata->priv;
102 
103     priv->ic = imcb_new(acct);
104     priv->ic->proto_data = fata;
105     return fata;
106 }
107 
108 gboolean
fb_data_load(FbData * fata)109 fb_data_load(FbData *fata)
110 {
111     account_t *acct;
112     const gchar *str;
113     FbDataPrivate *priv;
114     FbId id;
115     gboolean ret = TRUE;
116     guint i;
117     guint64 uint;
118     GValue val = G_VALUE_INIT;
119     int num;
120 
121     g_return_val_if_fail(FB_IS_DATA(fata), FALSE);
122     priv = fata->priv;
123     acct = priv->ic->acc;
124 
125     for (i = 0; i < G_N_ELEMENTS(fb_props_strs); i++) {
126         str = set_getstr(&acct->set, fb_props_strs[i]);
127 
128         if (str == NULL) {
129             ret = FALSE;
130         }
131 
132         g_value_init(&val, G_TYPE_STRING);
133         g_value_set_string(&val, str);
134         g_object_set_property(G_OBJECT(priv->api), fb_props_strs[i],
135                               &val);
136         g_value_unset(&val);
137     }
138 
139     str = set_getstr(&acct->set, "mid");
140 
141     if (str != NULL) {
142         uint = g_ascii_strtoull(str, NULL, 10);
143         g_value_init(&val, G_TYPE_UINT64);
144         g_value_set_uint64(&val, uint);
145         g_object_set_property(G_OBJECT(priv->api), "mid", &val);
146         g_value_unset(&val);
147     } else {
148         ret = FALSE;
149     }
150 
151     str = set_getstr(&acct->set, "uid");
152 
153     if (str != NULL) {
154         id = FB_ID_FROM_STR(str);
155         g_value_init(&val, FB_TYPE_ID);
156         g_value_set_int64(&val, id);
157         g_object_set_property(G_OBJECT(priv->api), "uid", &val);
158         g_value_unset(&val);
159     } else {
160         ret = FALSE;
161     }
162 
163     num = set_getint(&acct->set, "tweak");
164     if (num != 0) {
165         g_value_init(&val, G_TYPE_INT);
166         g_value_set_int(&val, num);
167         g_object_set_property(G_OBJECT(priv->api), "tweak", &val);
168         g_value_unset(&val);
169     }
170 
171     num = set_getbool(&acct->set, "work");
172     if (num != 0) {
173         g_value_init(&val, G_TYPE_BOOLEAN);
174         g_value_set_boolean(&val, num);
175         g_object_set_property(G_OBJECT(priv->api), "work", &val);
176         g_value_unset(&val);
177     }
178 
179     fb_api_rehash(priv->api);
180     return ret;
181 }
182 
183 void
fb_data_save(FbData * fata)184 fb_data_save(FbData *fata)
185 {
186     account_t *acct;
187     const gchar *str;
188     FbDataPrivate *priv;
189     gchar *dup;
190     guint i;
191     guint64 uint;
192     GValue val = G_VALUE_INIT;
193 
194     g_return_if_fail(FB_IS_DATA(fata));
195     priv = fata->priv;
196     acct = priv->ic->acc;
197 
198     for (i = 0; i < G_N_ELEMENTS(fb_props_strs); i++) {
199         g_value_init(&val, G_TYPE_STRING);
200         g_object_get_property(G_OBJECT(priv->api), fb_props_strs[i],
201                               &val);
202         str = g_value_get_string(&val);
203         set_setstr(&acct->set, fb_props_strs[i], (gchar *) str);
204         g_value_unset(&val);
205     }
206 
207     g_value_init(&val, G_TYPE_UINT64);
208     g_object_get_property(G_OBJECT(priv->api), "mid", &val);
209     uint = g_value_get_uint64(&val);
210     g_value_unset(&val);
211 
212     dup = g_strdup_printf("%" G_GINT64_FORMAT, uint);
213     set_setstr(&acct->set, "mid", dup);
214     g_free(dup);
215 
216     g_value_init(&val, G_TYPE_INT64);
217     g_object_get_property(G_OBJECT(priv->api), "uid", &val);
218     uint = g_value_get_int64(&val);
219     g_value_unset(&val);
220 
221     dup = g_strdup_printf("%" FB_ID_FORMAT, uint);
222     set_setstr(&acct->set, "uid", dup);
223     g_free(dup);
224 }
225 
226 void
fb_data_add_groupchat(FbData * fata,struct groupchat * gc)227 fb_data_add_groupchat(FbData *fata, struct groupchat *gc)
228 {
229     FbDataPrivate *priv;
230 
231     g_return_if_fail(FB_IS_DATA(fata));
232     priv = fata->priv;
233 
234     g_hash_table_replace(priv->gcs, gc, gc);
235 }
236 
237 void
fb_data_remove_groupchat(FbData * fata,struct groupchat * gc)238 fb_data_remove_groupchat(FbData *fata, struct groupchat *gc)
239 {
240     FbDataPrivate *priv;
241 
242     g_return_if_fail(FB_IS_DATA(fata));
243     priv = fata->priv;
244 
245     g_hash_table_remove(priv->gcs, gc);
246 }
247 
248 void
fb_data_add_thread_head(FbData * fata,FbId tid)249 fb_data_add_thread_head(FbData *fata, FbId tid)
250 {
251     FbDataPrivate *priv;
252     FbId *dtid;
253 
254     g_return_if_fail(FB_IS_DATA(fata));
255     priv = fata->priv;
256 
257     dtid = g_memdup(&tid, sizeof tid);
258     g_queue_push_head(priv->tids, dtid);
259 }
260 
261 void
fb_data_add_thread_tail(FbData * fata,FbId tid)262 fb_data_add_thread_tail(FbData *fata, FbId tid)
263 {
264     FbDataPrivate *priv;
265     FbId *dtid;
266 
267     g_return_if_fail(FB_IS_DATA(fata));
268     priv = fata->priv;
269 
270     dtid = g_memdup(&tid, sizeof tid);
271     g_queue_push_tail(priv->tids, dtid);
272 }
273 
274 void
fb_data_clear_threads(FbData * fata)275 fb_data_clear_threads(FbData *fata)
276 {
277     FbDataPrivate *priv;
278     FbId *tid;
279 
280     g_return_if_fail(FB_IS_DATA(fata));
281     priv = fata->priv;
282 
283     while (!g_queue_is_empty(priv->tids)) {
284         tid = g_queue_pop_head(priv->tids);
285         g_free(tid);
286     }
287 }
288 
289 FbId
fb_data_get_thread(FbData * fata,guint n)290 fb_data_get_thread(FbData *fata, guint n)
291 {
292     FbDataPrivate *priv;
293     FbId *tid;
294 
295     g_return_val_if_fail(FB_IS_DATA(fata), 0);
296     priv = fata->priv;
297 
298     tid = g_queue_peek_nth(priv->tids, n);
299 
300     if (tid == NULL) {
301         return 0;
302     }
303 
304     return *tid;
305 }
306 
307 void
fb_data_add_timeout(FbData * fata,const gchar * name,guint interval,b_event_handler func,gpointer data)308 fb_data_add_timeout(FbData *fata, const gchar *name, guint interval,
309                     b_event_handler func, gpointer data)
310 {
311     FbDataPrivate *priv;
312     gchar *key;
313     guint id;
314 
315     g_return_if_fail(FB_IS_DATA(fata));
316     priv = fata->priv;
317 
318     fb_data_clear_timeout(fata, name, TRUE);
319 
320     key = g_strdup(name);
321     id = b_timeout_add(interval, func, data);
322     g_hash_table_replace(priv->evs, key, GUINT_TO_POINTER(id));
323 }
324 
325 void
fb_data_clear_timeout(FbData * fata,const gchar * name,gboolean remove)326 fb_data_clear_timeout(FbData *fata, const gchar *name, gboolean remove)
327 {
328     FbDataPrivate *priv;
329     gpointer ptr;
330     guint id;
331 
332     g_return_if_fail(FB_IS_DATA(fata));
333     priv = fata->priv;
334 
335     ptr = g_hash_table_lookup(priv->evs, name);
336     id = GPOINTER_TO_UINT(ptr);
337 
338     if ((id > 0) && remove) {
339         b_event_remove(id);
340     }
341 
342     g_hash_table_remove(priv->evs, name);
343 }
344 
345 FbApi *
fb_data_get_api(FbData * fata)346 fb_data_get_api(FbData *fata)
347 {
348     FbDataPrivate *priv;
349 
350     g_return_val_if_fail(FB_IS_DATA(fata), NULL);
351     priv = fata->priv;
352 
353     return priv->api;
354 }
355 
356 struct im_connection *
fb_data_get_connection(FbData * fata)357 fb_data_get_connection(FbData *fata)
358 {
359     FbDataPrivate *priv;
360 
361     g_return_val_if_fail(FB_IS_DATA(fata), NULL);
362     priv = fata->priv;
363 
364     return priv->ic;
365 }
366 
367 void
fb_data_add_message(FbData * fata,FbApiMessage * msg)368 fb_data_add_message(FbData *fata, FbApiMessage *msg)
369 {
370     FbDataPrivate *priv;
371 
372     g_return_if_fail(FB_IS_DATA(fata));
373     priv = fata->priv;
374 
375     g_queue_push_tail(priv->msgs, msg);
376 }
377 
378 void
fb_data_remove_message(FbData * fata,FbApiMessage * msg)379 fb_data_remove_message(FbData *fata, FbApiMessage *msg)
380 {
381     FbDataPrivate *priv;
382 
383     g_return_if_fail(FB_IS_DATA(fata));
384     priv = fata->priv;
385 
386     g_queue_remove(priv->msgs, msg);
387 }
388 
389 GSList *
fb_data_take_messages(FbData * fata,FbId uid)390 fb_data_take_messages(FbData *fata, FbId uid)
391 {
392     FbApiMessage *msg;
393     FbDataPrivate *priv;
394     GList *l;
395     GList *prev;
396     GSList *msgs = NULL;
397 
398     g_return_val_if_fail(FB_IS_DATA(fata), NULL);
399     priv = fata->priv;
400     l = priv->msgs->tail;
401 
402     while (l != NULL) {
403         msg = l->data;
404         prev = l->prev;
405 
406         if (msg->uid == uid) {
407             msgs = g_slist_prepend(msgs, msg);
408             g_queue_delete_link(priv->msgs, l);
409         }
410 
411         l = prev;
412     }
413 
414     return msgs;
415 }
416