2
* * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>,
3
* * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
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.
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.
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.
20
#include <curl/curl.h>
21
#include <libxml/encoding.h>
24
#include <glib/gstdio.h>
25
#include "rs-facebook-client.h"
27
#define HTTP_BOUNDARY "4wncn84cq4ncto874ytnv90w43htn"
30
* Get a quark used to describe Facebook error domain
31
* @return A quark touse in GErrors from Facebook code
34
rs_facebook_client_error_quark(void)
36
static GStaticMutex lock = G_STATIC_MUTEX_INIT;
39
g_static_mutex_lock(&lock);
41
quark = g_quark_from_static_string("rawstudio_facebook_client_error");
42
g_static_mutex_unlock(&lock);
47
struct _RSFacebookClient {
60
G_DEFINE_TYPE(RSFacebookClient, rs_facebook_client, G_TYPE_OBJECT)
63
rs_facebook_client_finalize(GObject *object)
65
RSFacebookClient *facebook = RS_FACEBOOK_CLIENT(object);
67
g_free(facebook->session_key);
68
g_free(facebook->auth_token);
69
g_free(facebook->auth_url);
71
curl_easy_cleanup(facebook->curl);
73
G_OBJECT_CLASS(rs_facebook_client_parent_class)->finalize(object);
77
rs_facebook_client_class_init(RSFacebookClientClass *klass)
79
GObjectClass *object_class = G_OBJECT_CLASS(klass);
81
object_class->finalize = rs_facebook_client_finalize;
85
rs_facebook_client_init(RSFacebookClient *facebook)
87
facebook->curl = curl_easy_init();
91
xml_simple_response(const GString *xml, const gchar *needle, const gboolean root)
93
xmlDocPtr doc = xmlParseMemory(xml->str, xml->len);
96
cur = xmlDocGetRootElement(doc);
99
cur = cur->xmlChildrenNode;
101
gchar *result = NULL;
105
if ((!xmlStrcmp(cur->name, BAD_CAST(needle))))
106
result = (gchar *) xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
113
static GtkListStore *
114
xml_album_list_response(const GString *xml)
116
xmlDocPtr doc = xmlParseMemory(xml->str, xml->len);
117
xmlNodePtr cur, child;
119
cur = xmlDocGetRootElement(doc);
120
cur = cur->xmlChildrenNode;
126
GtkListStore *albums = NULL;
131
if ((!xmlStrcmp(cur->name, BAD_CAST("album"))))
133
child = cur->xmlChildrenNode;
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);
145
/* We can solely upload photos to normal albums (not profile, wall and such). */
146
if(g_strcmp0(type, "normal") == 0)
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 */
165
xml_error(const GString *xml, GError **error)
167
gchar *error_code = xml_simple_response(xml, "error_code", FALSE);
168
gchar *error_msg = xml_simple_response(xml, "error_msg", FALSE);
172
g_set_error(error, RS_FACEBOOK_CLIENT_ERROR_DOMAIN, 0, "%s", error_msg);
183
write_callback(void *ptr, size_t size, size_t nmemb, void *userp)
185
GString *string = (GString *) userp;
186
g_string_append_len(string, (char *) ptr, size * nmemb);
187
return (size * nmemb);
191
facebook_client_request(RSFacebookClient *facebook, const gchar *method, RSFacebookClientParam *param, GString *content, GError **error)
193
volatile static gint call_id = 0;
195
struct curl_slist *header = NULL;
196
gint post_length = 0;
199
/* We start by resetting all CURL parameters */
200
curl_easy_reset(facebook->curl);
203
curl_easy_setopt(facebook->curl, CURLOPT_VERBOSE, TRUE);
204
#endif /* fb_debug */
206
g_atomic_int_inc(&call_id);
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));
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);
218
header = curl_slist_append(header, "Content-Type: multipart/form-data; boundary=" HTTP_BOUNDARY);
219
header = curl_slist_append(header, "MIME-version: 1.0;");
221
post_str = rs_facebook_client_param_get_post(param, facebook->secret, HTTP_BOUNDARY, &post_length);
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);
231
curl_slist_free_all(header);
233
g_object_unref(param);
235
if (xml_error(content, error))
242
facebook_client_get_auth_token(RSFacebookClient *facebook, GError **error)
244
static GStaticMutex lock = G_STATIC_MUTEX_INIT;
246
g_static_mutex_lock(&lock);
247
if (!facebook->auth_token)
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);
254
g_static_mutex_unlock(&lock);
256
return facebook->auth_token;
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
267
rs_facebook_client_new(const gchar *api_key, const gchar *secret, const gchar *session_key)
269
RSFacebookClient *facebook = g_object_new(RS_TYPE_FACEBOOK_CLIENT, NULL);
271
facebook->api_key = api_key;
272
facebook->secret = secret;
274
rs_facebook_client_set_session_key(facebook, session_key);
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
288
rs_facebook_client_get_auth_url(RSFacebookClient *facebook, const gchar *base_url, GError **error)
290
static GStaticMutex lock = G_STATIC_MUTEX_INIT;
292
g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
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);
299
return facebook->auth_url;
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
309
rs_facebook_client_get_session_key(RSFacebookClient *facebook, GError **error)
311
static GStaticMutex lock = G_STATIC_MUTEX_INIT;
313
g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
315
g_static_mutex_lock(&lock);
316
RSFacebookClientParam *param = rs_facebook_client_param_new();
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);
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);
327
return facebook->session_key;
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
336
rs_facebook_client_set_session_key(RSFacebookClient *facebook, const gchar *session_key)
338
g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
340
g_free(facebook->session_key);
342
facebook->session_key = g_strdup(session_key);
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
352
rs_facebook_client_ping(RSFacebookClient *facebook, GError **error)
354
gboolean ret = FALSE;
355
g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
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);
362
if (result && g_str_equal(result, "1"))
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
379
rs_facebook_client_upload_image(RSFacebookClient *facebook, const gchar *filename, const gchar *caption, const gchar *aid, GError **error)
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);
385
RSFacebookClientParam *param = rs_facebook_client_param_new();
388
g_stat(filename, &st);
389
gchar *filesize = g_strdup_printf("%d", (gint) st.st_size);
391
rs_facebook_client_param_add_string(param, "filename", filename);
392
rs_facebook_client_param_add_string(param, "length", filesize);
395
rs_facebook_client_param_add_string(param, "caption", caption);
397
rs_facebook_client_param_add_string(param, "aid", aid);
399
GString *content = g_string_new("");
400
facebook_client_request(facebook, "facebook.photos.upload", param, content, error);
402
g_string_free(content, TRUE);
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
415
rs_facebook_client_get_album_list(RSFacebookClient *facebook, GError **error)
417
g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
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);
423
g_string_free(content, TRUE);
429
rs_facebook_client_create_album(RSFacebookClient *facebook, const gchar *album_name)
431
g_assert(RS_IS_FACEBOOK_CLIENT(facebook));
433
RSFacebookClientParam *param = rs_facebook_client_param_new();
434
rs_facebook_client_param_add_string(param, "name", album_name);
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);