~ubuntu-branches/ubuntu/vivid/rawstudio/vivid

« back to all changes in this revision

Viewing changes to plugins/output-facebook/rs-facebook-client.c

  • Committer: Bazaar Package Importer
  • Author(s): Bernd Zeimetz
  • Date: 2011-07-28 17:36:32 UTC
  • mfrom: (2.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20110728173632-5czluz9ye3c83zc5
Tags: 2.0-1
* [3750b2cf] Merge commit 'upstream/2.0'
* [63637468] Removing Patch, not necessary anymore.
* [2fb580dc] Add new build-dependencies.
* [c57d953b] Run dh_autoreconf due to patches in configure.in
* [13febe39] Add patch to remove the libssl requirement.
* [5ae773fe] Replace libjpeg62-dev by libjpeg8-dev :)
* [1969d755] Don't build static libraries.
* [7cfe0a2e] Add a patch to fix the plugin directory path.
  As plugins are shared libraries, they need to go into /usr/lib,
  not into /usr/share.
  Thanks to Andrew McMillan
* [c1d0d9dd] Don't install .la files for all plugins and libraries.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>, 
 
3
 * * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU General Public License
 
7
 * as published by the Free Software Foundation; either version 2
 
8
 * of the License, or (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
18
 */
 
19
 
 
20
#include <curl/curl.h>
 
21
#include <libxml/encoding.h>
 
22
#include <gtk/gtk.h>
 
23
#include <glib.h>
 
24
#include <glib/gstdio.h>
 
25
#include "rs-facebook-client.h"
 
26
 
 
27
#define HTTP_BOUNDARY "4wncn84cq4ncto874ytnv90w43htn"
 
28
 
 
29
/**
 
30
 * Get a quark used to describe Facebook error domain
 
31
 * @return A quark touse in GErrors from Facebook code
 
32
 */
 
33
GQuark
 
34
rs_facebook_client_error_quark(void)
 
35
{
 
36
        static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 
37
        static GQuark quark;
 
38
 
 
39
        g_static_mutex_lock(&lock);
 
40
        if (!quark)
 
41
                quark = g_quark_from_static_string("rawstudio_facebook_client_error");
 
42
        g_static_mutex_unlock(&lock);
 
43
 
 
44
        return quark;
 
45
}
 
46
 
 
47
struct _RSFacebookClient {
 
48
        GObject parent;
 
49
 
 
50
        const gchar *api_key;
 
51
        const gchar *secret;
 
52
 
 
53
        gchar *session_key;
 
54
        gchar *auth_token;
 
55
        gchar *auth_url;
 
56
 
 
57
        CURL *curl;
 
58
};
 
59
 
 
60
G_DEFINE_TYPE(RSFacebookClient, rs_facebook_client, G_TYPE_OBJECT)
 
61
 
 
62
static void
 
63
rs_facebook_client_finalize(GObject *object)
 
64
{
 
65
        RSFacebookClient *facebook = RS_FACEBOOK_CLIENT(object);
 
66
 
 
67
        g_free(facebook->session_key);
 
68
        g_free(facebook->auth_token);
 
69
        g_free(facebook->auth_url);
 
70
 
 
71
        curl_easy_cleanup(facebook->curl);
 
72
 
 
73
        G_OBJECT_CLASS(rs_facebook_client_parent_class)->finalize(object);
 
74
}
 
75
 
 
76
static void
 
77
rs_facebook_client_class_init(RSFacebookClientClass *klass)
 
78
{
 
79
        GObjectClass *object_class = G_OBJECT_CLASS(klass);
 
80
 
 
81
        object_class->finalize = rs_facebook_client_finalize;
 
82
}
 
83
 
 
84
static void
 
85
rs_facebook_client_init(RSFacebookClient *facebook)
 
86
{
 
87
        facebook->curl = curl_easy_init();
 
88
}
 
89
 
 
90
static gchar *
 
91
xml_simple_response(const GString *xml, const gchar *needle, const gboolean root)
 
92
{
 
93
        xmlDocPtr doc = xmlParseMemory(xml->str, xml->len);
 
94
        xmlNodePtr cur;
 
95
 
 
96
        cur = xmlDocGetRootElement(doc);
 
97
 
 
98
        if (!root && cur)
 
99
                cur = cur->xmlChildrenNode;
 
100
 
 
101
        gchar *result = NULL;
 
102
 
 
103
        while (cur)
 
104
        {
 
105
                if ((!xmlStrcmp(cur->name, BAD_CAST(needle))))
 
106
                        result = (gchar *) xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
 
107
 
 
108
                cur = cur->next;
 
109
        }
 
110
        return result;
 
111
}
 
112
 
 
113
static GtkListStore *
 
114
xml_album_list_response(const GString *xml)
 
115
{
 
116
        xmlDocPtr doc = xmlParseMemory(xml->str, xml->len);
 
117
        xmlNodePtr cur, child;
 
118
 
 
119
        cur = xmlDocGetRootElement(doc);
 
120
        cur = cur->xmlChildrenNode;
 
121
 
 
122
        gchar *name = NULL;
 
123
        gchar *aid = NULL;
 
124
        gchar *type = NULL;
 
125
 
 
126
        GtkListStore *albums = NULL;
 
127
        GtkTreeIter iter;
 
128
 
 
129
        while (cur)
 
130
        {
 
131
                if ((!xmlStrcmp(cur->name, BAD_CAST("album"))))
 
132
                {
 
133
                        child = cur->xmlChildrenNode;
 
134
                        while (child)
 
135
                        {
 
136
                                if ((!xmlStrcmp(child->name, BAD_CAST("name"))))
 
137
                                        name = (gchar *) xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
 
138
                                if ((!xmlStrcmp(child->name, BAD_CAST("aid"))))
 
139
                                        aid = (gchar *) xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
 
140
                                if ((!xmlStrcmp(child->name, BAD_CAST("type"))))
 
141
                                        type = (gchar *) xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
 
142
                                child = child->next;
 
143
                        }
 
144
 
 
145
                        /* We can solely upload photos to normal albums (not profile, wall and such). */
 
146
                        if(g_strcmp0(type, "normal") == 0)
 
147
                        {
 
148
                                if (!albums)
 
149
                                        albums = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
 
150
                                gtk_list_store_append(albums, &iter);
 
151
                                gtk_list_store_set(albums, &iter,
 
152
                                                   0, name, /* FIXME: Hardcoded */
 
153
                                                   1, aid, /* FIXME: Hardcoded */
 
154
                                                   -1);
 
155
                        }
 
156
                        g_free(type);
 
157
                }
 
158
 
 
159
                cur = cur->next;
 
160
        }
 
161
        return albums;
 
162
}
 
163
 
 
164
static gboolean
 
165
xml_error(const GString *xml, GError **error)
 
166
{
 
167
        gchar *error_code = xml_simple_response(xml, "error_code", FALSE);
 
168
        gchar *error_msg = xml_simple_response(xml, "error_msg", FALSE);
 
169
 
 
170
        if (error_code)
 
171
        {
 
172
                g_set_error(error, RS_FACEBOOK_CLIENT_ERROR_DOMAIN, 0, "%s", error_msg);
 
173
                g_free(error_code);
 
174
                g_free(error_msg);
 
175
                return TRUE;
 
176
        }
 
177
        g_free(error_code);
 
178
        g_free(error_msg);
 
179
        return FALSE;
 
180
}
 
181
 
 
182
static size_t
 
183
write_callback(void *ptr, size_t size, size_t nmemb, void *userp)
 
184
{
 
185
    GString *string = (GString *) userp;
 
186
    g_string_append_len(string, (char *) ptr, size * nmemb);
 
187
    return (size * nmemb);
 
188
}
 
189
 
 
190
static gboolean
 
191
facebook_client_request(RSFacebookClient *facebook, const gchar *method, RSFacebookClientParam *param, GString *content, GError **error)
 
192
{
 
193
        volatile static gint call_id = 0;
 
194
        CURLcode result;
 
195
        struct curl_slist *header = NULL;
 
196
        gint post_length = 0;
 
197
        gchar *post_str;
 
198
 
 
199
        /* We start by resetting all CURL parameters */
 
200
        curl_easy_reset(facebook->curl);
 
201
 
 
202
#ifdef fb_debug
 
203
        curl_easy_setopt(facebook->curl, CURLOPT_VERBOSE, TRUE);
 
204
#endif /* fb_debug */
 
205
 
 
206
        g_atomic_int_inc(&call_id);
 
207
 
 
208
        curl_easy_setopt(facebook->curl, CURLOPT_URL, "api.facebook.com/restserver.php");
 
209
        rs_facebook_client_param_add_string(param, "api_key", facebook->api_key);
 
210
        rs_facebook_client_param_add_string(param, "method", method);
 
211
        rs_facebook_client_param_add_string(param, "v", "1.0");
 
212
        rs_facebook_client_param_add_integer(param, "call_id", g_atomic_int_get(&call_id));
 
213
 
 
214
        /* If we have a session key, we will use it */
 
215
        if(facebook->session_key)
 
216
                rs_facebook_client_param_add_string(param, "session_key", facebook->session_key);
 
217
 
 
218
        header = curl_slist_append(header, "Content-Type: multipart/form-data; boundary=" HTTP_BOUNDARY);
 
219
        header = curl_slist_append(header, "MIME-version: 1.0;");
 
220
 
 
221
        post_str = rs_facebook_client_param_get_post(param, facebook->secret, HTTP_BOUNDARY, &post_length);
 
222
 
 
223
        curl_easy_setopt(facebook->curl, CURLOPT_POST, TRUE);
 
224
        curl_easy_setopt(facebook->curl, CURLOPT_POSTFIELDS, post_str);
 
225
        curl_easy_setopt(facebook->curl, CURLOPT_POSTFIELDSIZE, post_length);
 
226
        curl_easy_setopt(facebook->curl, CURLOPT_WRITEFUNCTION, write_callback);
 
227
        curl_easy_setopt(facebook->curl, CURLOPT_WRITEDATA, content);
 
228
        curl_easy_setopt(facebook->curl, CURLOPT_HTTPHEADER, header);
 
229
        result = curl_easy_perform(facebook->curl);
 
230
 
 
231
        curl_slist_free_all(header);
 
232
        g_free(post_str);
 
233
        g_object_unref(param);
 
234
 
 
235
        if (xml_error(content, error))
 
236
                return FALSE;
 
237
 
 
238
        return (result==0);
 
239
}
 
240
 
 
241
static const gchar *
 
242
facebook_client_get_auth_token(RSFacebookClient *facebook, GError **error)
 
243
{
 
244
        static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 
245
 
 
246
        g_static_mutex_lock(&lock);
 
247
        if (!facebook->auth_token)
 
248
        {
 
249
                GString *content = g_string_new("");
 
250
                facebook_client_request(facebook, "facebook.auth.createToken", rs_facebook_client_param_new(), content, error);
 
251
                facebook->auth_token = xml_simple_response(content, "auth_createToken_response", TRUE);
 
252
                g_string_free(content, TRUE);
 
253
        }
 
254
        g_static_mutex_unlock(&lock);
 
255
 
 
256
        return facebook->auth_token;
 
257
}
 
258
 
 
259
/**
 
260
 * Initializes a new RSFacebookClient
 
261
 * @param api_key The API key from Facebook
 
262
 * @param secret The secret provided by Facebook
 
263
 * @param session_key The stored session key or NULL if you haven't got one yet
 
264
 * @return A new RSFacebookClient, this must be unreffed
 
265
 */
 
266
RSFacebookClient *
 
267
rs_facebook_client_new(const gchar *api_key, const gchar *secret, const gchar *session_key)
 
268
{
 
269
        RSFacebookClient *facebook = g_object_new(RS_TYPE_FACEBOOK_CLIENT, NULL);
 
270
 
 
271
        facebook->api_key = api_key;
 
272
        facebook->secret = secret;
 
273
 
 
274
        rs_facebook_client_set_session_key(facebook, session_key);
 
275
 
 
276
        return facebook;
 
277
}
 
278
 
 
279
 
 
280
/**
 
281
 * Get the url that the user must visit to authenticate this application (api_key)
 
282
 * @param facebook A RSFacebookClient
 
283
 * @param base_url A prefix URL, "http://api.facebook.com/login.php" would make sense
 
284
 * @param error NULL or a pointer to a GError * initialized to NULL
 
285
 * @return A URL that the user can visit to authenticate this application. Thisshould not be freed
 
286
 */
 
287
const gchar *
 
288
rs_facebook_client_get_auth_url(RSFacebookClient *facebook, const gchar *base_url, GError **error)
 
289
{
 
290
        static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 
291
 
 
292
        g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
 
293
 
 
294
        g_static_mutex_lock(&lock);
 
295
        if (!facebook->auth_url)
 
296
                facebook->auth_url = g_strdup_printf("%s?api_key=%s&auth_token=%s&req_perms=user_photos", base_url, facebook->api_key, facebook_client_get_auth_token(facebook, error));
 
297
        g_static_mutex_unlock(&lock);
 
298
 
 
299
        return facebook->auth_url;
 
300
}
 
301
 
 
302
/**
 
303
 * Get the session key as returned from Facebook
 
304
 * @param facebook A RSFacebookClient
 
305
 * @param error NULL or a pointer to a GError * initialized to NULL
 
306
 * @return The session key from Facebook or NULL on error
 
307
 */
 
308
const gchar *
 
309
rs_facebook_client_get_session_key(RSFacebookClient *facebook, GError **error)
 
310
{
 
311
        static GStaticMutex lock = G_STATIC_MUTEX_INIT;
 
312
 
 
313
        g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
 
314
 
 
315
        g_static_mutex_lock(&lock);
 
316
        RSFacebookClientParam *param = rs_facebook_client_param_new();
 
317
 
 
318
        rs_facebook_client_param_add_string(param, "auth_token", facebook->auth_token);
 
319
        GString *content = g_string_new("");
 
320
        facebook_client_request(facebook, "facebook.auth.getSession", param, content, error);
 
321
 
 
322
        g_free(facebook->session_key);
 
323
        facebook->session_key = xml_simple_response(content, "session_key", FALSE);
 
324
        g_string_free(content, TRUE);
 
325
        g_static_mutex_unlock(&lock);
 
326
 
 
327
        return facebook->session_key;
 
328
}
 
329
 
 
330
/**
 
331
 * Set the session key, this can be used to cache the session_key
 
332
 * @param facebook A RSFacebookClient
 
333
 * @param session_key A new session key to use
 
334
 */
 
335
void
 
336
rs_facebook_client_set_session_key(RSFacebookClient *facebook, const gchar *session_key)
 
337
{
 
338
        g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
 
339
 
 
340
        g_free(facebook->session_key);
 
341
 
 
342
        facebook->session_key = g_strdup(session_key);
 
343
}
 
344
 
 
345
/**
 
346
 * Check if we are authenticated to Facebook
 
347
 * @param facebook A RSFacebookClient
 
348
 * @param error NULL or a pointer to a GError * initialized to NULL
 
349
 * @return TRUE if we're authenticated both by the Facebook API and by the end-user, FALSE otherwise
 
350
 */
 
351
gboolean
 
352
rs_facebook_client_ping(RSFacebookClient *facebook, GError **error)
 
353
{
 
354
        gboolean ret = FALSE;
 
355
        g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
 
356
 
 
357
        GString *content = g_string_new("");
 
358
        facebook_client_request(facebook, "facebook.users.isAppAdded", rs_facebook_client_param_new(), content, NULL);
 
359
        gchar *result = xml_simple_response(content, "users_isAppAdded_response", TRUE);
 
360
        g_string_free(content, TRUE);
 
361
 
 
362
        if (result && g_str_equal(result, "1"))
 
363
                ret = TRUE;
 
364
 
 
365
        g_free(result);
 
366
 
 
367
        return ret;
 
368
}
 
369
 
 
370
/**
 
371
 * Upload a photo to Facebook, will be placed in the registered applications default photo folder
 
372
 * @param facebook A RSFacebookClient
 
373
 * @param filename Full path to an image to upload. JPEG, PNG, TIFF accepted
 
374
 * @param caption The caption to use for the image
 
375
 * @param error NULL or a pointer to a GError * initialized to NULL
 
376
 * @return TRUE on success, FALSE otherwise
 
377
 */
 
378
gboolean
 
379
rs_facebook_client_upload_image(RSFacebookClient *facebook, const gchar *filename, const gchar *caption, const gchar *aid, GError **error)
 
380
{
 
381
        g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
 
382
        g_return_val_if_fail(filename != NULL, FALSE);
 
383
        g_return_val_if_fail(g_path_is_absolute(filename), FALSE);
 
384
 
 
385
        RSFacebookClientParam *param = rs_facebook_client_param_new();
 
386
 
 
387
        struct stat st;
 
388
        g_stat(filename, &st);
 
389
        gchar *filesize = g_strdup_printf("%d", (gint) st.st_size);
 
390
 
 
391
        rs_facebook_client_param_add_string(param, "filename", filename);
 
392
        rs_facebook_client_param_add_string(param, "length", filesize);
 
393
 
 
394
        if (caption)
 
395
                rs_facebook_client_param_add_string(param, "caption", caption);
 
396
        if (aid)
 
397
                rs_facebook_client_param_add_string(param, "aid", aid);
 
398
 
 
399
        GString *content = g_string_new("");
 
400
        facebook_client_request(facebook, "facebook.photos.upload", param, content, error);
 
401
 
 
402
        g_string_free(content, TRUE);
 
403
        g_free(filesize);
 
404
 
 
405
        return TRUE;
 
406
}
 
407
 
 
408
/**
 
409
 * Get list of available albums on Facebook account (not profile, wall and so on)
 
410
 * @param facebook A RSFacebookClient
 
411
 * @param error NULL or a pointer to a GError * initialized to NULL
 
412
 * @return a GtkListStore with albums if any, NULL otherwise
 
413
 */
 
414
GtkListStore *
 
415
rs_facebook_client_get_album_list(RSFacebookClient *facebook, GError **error)
 
416
{
 
417
        g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
 
418
 
 
419
        GString *content = g_string_new("");
 
420
        facebook_client_request(facebook, "facebook.photos.getAlbums", rs_facebook_client_param_new(), content, error);
 
421
        GtkListStore *albums = xml_album_list_response(content);
 
422
 
 
423
        g_string_free(content, TRUE);
 
424
 
 
425
        return albums;
 
426
}
 
427
 
 
428
gchar *
 
429
rs_facebook_client_create_album(RSFacebookClient *facebook, const gchar *album_name)
 
430
{
 
431
        g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
 
432
 
 
433
        RSFacebookClientParam *param = rs_facebook_client_param_new();
 
434
        rs_facebook_client_param_add_string(param, "name", album_name);
 
435
 
 
436
        GString *content = g_string_new("");
 
437
        facebook_client_request(facebook, "facebook.photos.createAlbum", param, content, NULL);
 
438
        gchar *aid = xml_simple_response(content, "aid", FALSE);
 
439
        g_string_free(content, TRUE);
 
440
        return aid;
 
441
}