Add support for mobypicture
[maemo-sharing-twitter-multi:maemo-sharing-twitter-multi.git] / src / twitpic.c
1 /*
2  * This file is part of maemo-sharing-twitter-multi
3  *
4  * Copyright (C) 2010 Igalia, S.L.
5  * Authors: Alberto Garcia <agarcia@igalia.com>
6  *
7  * This is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 3 as
9  * published by the Free Software Foundation.
10  *
11  * This software is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this software. If not, see <http://www.gnu.org/licenses/>
18  */
19
20 #include "twitpic.h"
21 #include "util.h"
22
23 #include <hildon-mime.h>
24 #include <sharing-http.h>
25 #include <libxml/parser.h>
26
27 #define TWITPIC_API_KEY                 "1f9ce28260728df0a40cafe3506a9407"
28 #define MOBYPICTURE_API_KEY             "L5RL7tAoAAsgZqKP"
29
30 static gchar *
31 parse_server_response                   (const gchar       *response,
32                                          gsize              size,
33                                          TwitterPicService  service)
34 {
35   xmlDoc *doc;
36   const gchar *rootid, *urlid;
37   gchar *url = NULL;
38
39   g_return_val_if_fail (response != NULL, NULL);
40
41   switch (service)
42     {
43     case SERVICE_TWITPIC:
44       rootid = "image";
45       urlid  = "url";
46       break;
47     case SERVICE_MOBYPICTURE:
48     case SERVICE_TWITGOO:
49       rootid = "rsp";
50       urlid  = "mediaurl";
51       break;
52     }
53
54   doc = xmlParseMemory (response, size);
55   if (doc != NULL)
56     {
57       xmlNode *iter, *root = NULL;
58
59       for (iter = xmlDocGetRootElement (doc); iter && !root; iter = iter->next)
60         if (xmlStrEqual (iter->name, (xmlChar *) rootid))
61           root = iter;
62
63       if (root != NULL)
64         for (iter = root->xmlChildrenNode; iter && !url; iter = iter->next)
65           if (xmlStrEqual (iter->name, (xmlChar *) urlid))
66             {
67               xmlChar *s = xmlNodeGetContent (iter);
68               url = g_strdup ((gchar *) s);
69               xmlFree (s);
70             }
71
72       xmlFreeDoc (doc);
73     }
74
75   return url;
76 }
77
78 static void
79 open_auth_url                           (SharingAccount *account)
80 {
81   gchar *url = twitter_get_auth_url (account);
82   if (url)
83     {
84       hildon_uri_open (url, NULL, NULL);
85       g_free (url);
86     }
87 }
88
89 static void
90 register_account_clicked                (GtkWidget      *button,
91                                          SharingAccount *account)
92 {
93   open_auth_url (account);
94   gtk_dialog_response (GTK_DIALOG (gtk_widget_get_toplevel (button)),
95                        GTK_RESPONSE_ACCEPT);
96 }
97
98 static gboolean
99 twitpic_account_enter_pin               (SharingAccount *account,
100                                          GtkWindow      *parent)
101 {
102   gint response;
103   gchar *pin = NULL;
104   GtkWidget *d, *label, *entry, *hbox;
105
106   g_return_val_if_fail (account != NULL, FALSE);
107
108   d = gtk_dialog_new ();
109   hbox = gtk_hbox_new (FALSE, 0);
110   gtk_window_set_title (GTK_WINDOW (d), "Account setup - Twitter PIN number");
111   gtk_window_set_transient_for (GTK_WINDOW (d), parent);
112   gtk_dialog_add_button (GTK_DIALOG (d), GTK_STOCK_OK, GTK_RESPONSE_ACCEPT);
113
114   label = gtk_label_new ("Enter PIN number:");
115   entry = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
116   gtk_entry_set_width_chars (GTK_ENTRY (entry), 10);
117
118   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (d)->vbox), hbox);
119   gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
120   gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
121
122   gtk_widget_show_all (d);
123   response = gtk_dialog_run (GTK_DIALOG (d));
124
125   if (response == GTK_RESPONSE_ACCEPT)
126     pin = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))));
127
128   twitter_account_set_pin (account, pin);
129
130   g_free (pin);
131   gtk_widget_destroy (d);
132
133   return (response == GTK_RESPONSE_ACCEPT);
134 }
135
136 gboolean
137 twitpic_account_setup                   (SharingAccount *account,
138                                          GtkWindow      *parent)
139 {
140   gint response;
141   gboolean success = FALSE;
142   GtkWidget *d, *label, *button;
143   GtkContainer *vbox;
144
145   d = gtk_dialog_new ();
146   vbox = GTK_CONTAINER (GTK_DIALOG (d)->vbox);
147   gtk_window_set_title (GTK_WINDOW (d), "Account setup - Twitpic");
148   gtk_window_set_transient_for (GTK_WINDOW (d), parent);
149
150   label = gtk_label_new ("You need to register a Twitter account using the web page.\n"
151                          "If you don't have one, you'll be offered the option to do it.\n"
152                          "You'll be given a PIN number to continue this setup.");
153   button = gtk_button_new_with_label ("Register Twitter account");
154
155   hildon_gtk_widget_set_theme_size (button, HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
156
157   gtk_container_add (vbox, label);
158   gtk_container_add (vbox, button);
159
160   g_signal_connect (button, "clicked", G_CALLBACK (register_account_clicked), account);
161
162   gtk_widget_show_all (d);
163   response = gtk_dialog_run (GTK_DIALOG (d));
164   gtk_widget_destroy (d);
165
166   if (response != GTK_RESPONSE_DELETE_EVENT)
167     success = twitpic_account_enter_pin (account, parent);
168
169   return success;
170 }
171
172
173 gboolean
174 twitpic_account_validate                (SharingAccount *account,
175                                          gboolean       *dead_mans_switch)
176 {
177   *dead_mans_switch = FALSE;
178   return twitter_account_validate (account);
179 }
180
181 typedef struct {
182   SharingTransfer *transfer;
183   gboolean        *dead_mans_switch;
184   guint64          size;
185 } UploadProgressData;
186
187 static gboolean
188 upload_progress_cb                      (SharingHTTP *http,
189                                          guint64      bytes_sent,
190                                          gpointer     user_data)
191 {
192   UploadProgressData *data = user_data;
193   gdouble progress = 0.0;
194   *(data->dead_mans_switch) = FALSE;
195   if (data->size > 0)
196     progress = (gdouble) bytes_sent / data->size;
197   sharing_transfer_set_progress (data->transfer, CLAMP (progress, 0.0, 1.0));
198   return TRUE;
199 }
200
201 SharingPluginInterfaceSendResult
202 twitpic_share_file                      (SharingTransfer *transfer,
203                                          ConIcConnection *con,
204                                          gboolean        *dead_mans_switch)
205 {
206   SharingPluginInterfaceSendResult retval;
207   SharingEntry *entry;
208   const GSList *l;
209   const gchar *servicename;
210   TwitterPicService service = SERVICE_TWITPIC;
211
212   retval = SHARING_SEND_SUCCESS;
213   *dead_mans_switch = FALSE;
214   sharing_transfer_set_progress (transfer, 0.0);
215
216   entry = sharing_transfer_get_entry (transfer);
217   l = sharing_entry_get_media (entry);
218
219   servicename = sharing_entry_get_option (entry, "service");
220   if (servicename)
221     {
222       if (g_str_equal (servicename, "twitgoo"))
223         service = SERVICE_TWITGOO;
224       else if (g_str_equal (servicename, "mobypicture"))
225         service = SERVICE_MOBYPICTURE;
226     }
227
228   for (; l != NULL && retval == SHARING_SEND_SUCCESS; l = l->next)
229     {
230       const gchar *path;
231       gchar *mime;
232       SharingAccount *account;
233       SharingEntryMedia *media = l->data;
234
235       g_return_val_if_fail (media != NULL, SHARING_SEND_ERROR_UNKNOWN);
236
237       path = sharing_entry_media_get_localpath (media);
238       mime = sharing_entry_media_get_mime (media);
239       account = sharing_entry_get_account (entry);
240
241       if (path && mime && !sharing_entry_media_get_sent (media) &&
242           twitter_account_validate (account))
243         {
244           const gchar *posturl;
245           gchar *hdr, *title;
246           SharingHTTP *http = sharing_http_new ();
247           SharingHTTPRunResponse httpret;
248           UploadProgressData data;
249
250           data.transfer = transfer;
251           data.dead_mans_switch = dead_mans_switch;
252           data.size = sharing_entry_media_get_size (media);
253
254           title = sharing_entry_media_get_title (media);
255           if (title)
256             g_strstrip (title);
257           else
258             title = g_strdup ("Photo: ");
259
260           sharing_http_set_progress_callback (http, upload_progress_cb, &data);
261           sharing_http_add_req_multipart_file (http, "media", path, mime);
262           sharing_http_add_req_multipart_data (http, "message", title, -1, "text/plain");
263
264           switch (service)
265             {
266             case SERVICE_TWITPIC:
267               sharing_http_add_req_multipart_data (http, "key", TWITPIC_API_KEY, -1, "text/plain");
268               posturl = "http://api.twitpic.com/2/upload.xml";
269               break;
270             case SERVICE_MOBYPICTURE:
271               sharing_http_add_req_multipart_data (http, "key", MOBYPICTURE_API_KEY, -1, "text/plain");
272               posturl = "https://api.mobypicture.com/2.0/upload.xml";
273               break;
274             case SERVICE_TWITGOO:
275               posturl = "http://twitgoo.com/api/upload";
276               break;
277             default:
278               g_return_val_if_reached (SHARING_SEND_ERROR_UNKNOWN);
279             }
280
281           hdr = twitter_get_verify_credentials_header (account);
282           sharing_http_add_req_header_line (http, hdr);
283           g_free (hdr);
284
285           sharing_http_add_req_header_line (http, "X-Auth-Service-Provider: " TWITTER_VERIFY_CREDENTIALS_URL);
286           httpret = sharing_http_run (http, posturl);
287
288           switch (httpret)
289             {
290             case SHARING_HTTP_RUNRES_SUCCESS:
291               {
292                 gsize len;
293                 const gchar *body;
294                 gchar *img_url;
295
296                 body = sharing_http_get_res_body (http, &len);
297                 img_url = parse_server_response (body, len, service);
298
299                 if (img_url != NULL)
300                   {
301                     gchar *tweet = g_strconcat (title, " ", img_url, NULL);
302
303                     if (twitter_update_status (tweet, account))
304                       sharing_entry_media_set_sent (media, TRUE);
305
306                     g_free (img_url);
307                     g_free (tweet);
308                   }
309                 else
310                   {
311                     retval = SHARING_SEND_ERROR_UNKNOWN;
312                   }
313               }
314               break;
315             case SHARING_HTTP_RUNRES_CANCELLED:
316               retval = SHARING_SEND_CANCELLED;
317               break;
318             case SHARING_HTTP_RUNRES_CONNECTION_PROBLEM:
319               retval = SHARING_SEND_ERROR_CONNECTION;
320               break;
321             default:
322               retval = SHARING_SEND_ERROR_UNKNOWN;
323             }
324
325           sharing_http_unref (http);
326           g_free (title);
327         }
328
329       sharing_account_free (account);
330       g_free (mime);
331     }
332
333   return retval;
334 }
335
336 SharingPluginInterfaceEditAccountResult
337 twitpic_account_edit                    (GtkWindow       *parent,
338                                          SharingAccount  *account,
339                                          ConIcConnection *con,
340                                          gboolean        *dead_mans_switch)
341 {
342   gint response;
343   GtkWidget *d, *label;
344   enum { RESPONSE_EDIT, RESPONSE_REMOVE };
345
346   g_return_val_if_fail (account && dead_mans_switch, SHARING_EDIT_ACCOUNT_ERROR_UNKNOWN);
347
348   d = gtk_dialog_new ();
349   gtk_window_set_title (GTK_WINDOW (d), "Edit account - Twitpic");
350   gtk_window_set_transient_for (GTK_WINDOW (d), parent);
351   gtk_dialog_add_button (GTK_DIALOG (d), GTK_STOCK_REMOVE, RESPONSE_REMOVE);
352   gtk_dialog_add_button (GTK_DIALOG (d), GTK_STOCK_EDIT, RESPONSE_EDIT);
353
354   label = gtk_label_new ("Press 'Edit' to open the Twitter web page.\n"
355                          "After that, enter the PIN number here\n"
356                          "to confirm your changes.");
357
358   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (d)->vbox), label);
359
360   gtk_widget_show_all (d);
361   response = gtk_dialog_run (GTK_DIALOG (d));
362   gtk_widget_destroy (d);
363
364   switch (response)
365     {
366     case RESPONSE_EDIT:
367       open_auth_url (account);
368       if (twitpic_account_enter_pin (account, parent))
369         {
370           return SHARING_EDIT_ACCOUNT_SUCCESS;
371         }
372       else
373         {
374           return SHARING_EDIT_ACCOUNT_CANCELLED;
375         }
376     case RESPONSE_REMOVE:
377       return SHARING_EDIT_ACCOUNT_DELETE;
378     case GTK_RESPONSE_DELETE_EVENT:
379       return SHARING_EDIT_ACCOUNT_NOT_STARTED;
380     }
381
382   return SHARING_EDIT_ACCOUNT_ERROR_UNKNOWN;
383 }