v0.2.2 release
[yandexfotkisp] / src / common.cc
1 /*
2  * This file is part of sharing-plugin-template
3  *
4  * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved.
5  *
6  * This maemo code example is licensed under a MIT-style license,
7  * that can be found in the file called "COPYING" in the root
8  * directory.
9  *
10  */
11
12 #include <stdio.h>
13 #include <glib.h>
14 #include <osso-log.h>
15 #include "common.h"
16 #include <libxml/parser.h>
17 #include <libxml/xpath.h>
18 #include <curl/curl.h>
19 #include <string>
20 #include <sys/stat.h>
21 #include "libsharing/sharing-tag.h"
22 #include "libsharing/sharing-service-option.h"
23 #include "base64.h"
24 #include "CP_RSA.h"
25
26 static size_t getUrlContentWriteFunction(void *ptr, size_t size, size_t nmemb, void *data);
27 std::string   getUrlContent(const char* url,const char* token);
28 std::string getXmlElementValueByXPath(xmlDocPtr xmlDoc, const char* xmlXPath);
29 static gchar* createTagsStr (const GSList* tags);
30
31 yandexGetSessionKeyResult yandexGetSessionKey(char** key, char** request_id) {
32         yandexGetSessionKeyResult ret = YANDEX_GET_SESSION_KEY_FAILED;
33         std::string getSessionKeyRequestBody = getUrlContent("http://auth.mobile.yandex.ru/yamrsa/key/",NULL);
34         if (!getSessionKeyRequestBody.empty()) {
35         xmlDocPtr xmlDoc = NULL;
36         xmlDoc = xmlReadMemory(getSessionKeyRequestBody.c_str(), (int) getSessionKeyRequestBody.length(), "yandexGetSessionKey.xml", NULL, 0);
37         if (xmlDoc != NULL) {
38                 std::string str_key = getXmlElementValueByXPath(xmlDoc, "/response/key");
39                 std::string str_request_id = getXmlElementValueByXPath(xmlDoc, "/response/request_id");
40                 if (!str_key.empty() && !str_request_id.empty()) {
41                         ret = YANDEX_GET_SESSION_KEY_SUCCESS;
42                         *key = (char*) malloc(str_key.length()+1);
43                         if (*key) strcpy(*key, str_key.c_str());
44                         *request_id = (char*) malloc(str_request_id.length()+1);
45                         if (*request_id) strcpy(*request_id, str_request_id.c_str());
46                 }
47                 xmlFreeDoc(xmlDoc);
48         }
49         }
50         return ret;
51 }
52
53 yandexGetAuthTokenResult yandexGetAuthToken(const char* request_id, const char* key, const char* username, const char* password, char** token) {
54         yandexGetAuthTokenResult ret = YANDEX_GET_AUTH_TOKEN_FAILED;
55
56         /* Build crypted base64 encoded credentials string */
57         CCryptoProviderRSA encrypter;
58         encrypter.ImportPublicKey(key);
59         std::string credentials("<credentials login=\"");
60         credentials += username;
61         credentials += "\" password=\"";
62         credentials += password;
63         credentials += "\"/>";
64         char crypted_credentials[MAX_CRYPT_BITS / sizeof(char)] = "\0";
65         size_t crypted_credentials_length = 0;
66         encrypter.Encrypt(credentials.c_str(), credentials.size(), crypted_credentials, crypted_credentials_length);
67         std::string b64_crypted_credentials = base64_encode((unsigned char *)crypted_credentials, crypted_credentials_length);
68
69         /* Send request */
70         CURL *curl;
71         CURLcode res;
72         std::string body;
73         struct curl_httppost *formpost = NULL;
74         struct curl_httppost *lastptr = NULL;
75         struct curl_slist *headerlist=NULL;
76         static const char expectHeader[] = "Expect:";
77         curl_formadd(&formpost,
78                      &lastptr,
79                      CURLFORM_COPYNAME, "request_id",
80                                  CURLFORM_COPYCONTENTS, request_id,
81                      CURLFORM_END);
82         curl_formadd(&formpost,
83                      &lastptr,
84                      CURLFORM_COPYNAME, "credentials",
85                                  CURLFORM_COPYCONTENTS, b64_crypted_credentials.c_str(),
86                      CURLFORM_END);
87         curl = curl_easy_init();
88         if (curl) {
89                 headerlist = curl_slist_append(headerlist, expectHeader);
90                 curl_easy_setopt(curl, CURLOPT_URL, "http://auth.mobile.yandex.ru/yamrsa/token/");
91                 curl_easy_setopt(curl, CURLOPT_USERAGENT, PLUGIN_USER_AGENT);
92                 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
93                 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
94                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, getUrlContentWriteFunction);
95                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body);
96                 res = curl_easy_perform(curl);
97                 curl_easy_cleanup(curl);
98                 curl_formfree(formpost);
99                 curl_slist_free_all (headerlist);
100                 if (CURLE_OK == res && !body.empty()) {
101                         std::string str_token;
102                         xmlDocPtr xmlDoc = NULL;
103                         xmlDoc = xmlReadMemory(body.c_str(), body.length(), "yandexGetAuthToken.xml", NULL, 0);
104                         if (xmlDoc != NULL) {
105                                 str_token = getXmlElementValueByXPath(xmlDoc,"/response/token");
106                                 if (str_token.empty()) ret = YANDEX_GET_AUTH_TOKEN_INVALID_USER;
107                                 else {
108                                         ret = YANDEX_GET_AUTH_TOKEN_SUCCESS;
109                                         *token = (char*) malloc(str_token.length()+1);
110                                         if (*token) strcpy(*token, str_token.c_str());
111                                 }
112                                 xmlFreeDoc(xmlDoc);
113                         }
114                 }
115         }
116         return ret;
117 }
118
119 int curlProgressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
120         SharingTransfer* transfer = (SharingTransfer*) clientp;
121         gdouble progress = 0;
122         if (ultotal > 0) progress = ulnow / ultotal;
123         if (transfer) sharing_transfer_set_progress(transfer,progress);
124         return 0;
125 }
126
127 yandexSendPhotoResult yandexSendPhoto(const char* token, const SharingEntryMedia* photo, SharingTransfer* transfer, yandexPhotoOptions options) {
128         yandexSendPhotoResult ret = YANDEX_SEND_PHOTO_FAILED;
129
130         const gchar* filepath = sharing_entry_media_get_localpath(photo);
131         gchar* filename = sharing_entry_media_get_filename(photo);
132         gchar* title = sharing_entry_media_get_title(photo);
133         gchar* contentType = sharing_entry_media_get_mime(photo);
134         gchar* tags = createTagsStr(sharing_entry_media_get_tags (photo));
135         std::string access_type = "public";
136         if (options.access == YANDEX_PHOTO_ACCESS_FRIENDS) access_type = "friends";
137         else if (options.access == YANDEX_PHOTO_ACCESS_PRIVATE) access_type = "private";
138         std::string publish = "0";
139         if (options.publish == YANDEX_PHOTO_PUBLISH) publish = "1";
140
141         struct stat fileStat;
142         if (stat(filepath,&fileStat) == 0) {
143                 CURL *curl;
144                 CURLcode res;
145                 std::string body;
146                 struct curl_httppost *formpost = NULL;
147                 struct curl_httppost *lastptr = NULL;
148                 struct curl_slist *headerlist=NULL;
149                 static const char expectHeader[] = "Expect:";
150                 curl_formadd(&formpost,
151                                          &lastptr,
152                                          CURLFORM_COPYNAME, "image",
153                                          CURLFORM_FILE, filepath,
154                                          CURLFORM_FILENAME, filename,
155                                          CURLFORM_CONTENTTYPE, contentType,
156                                          CURLFORM_END);
157                 if (options.album) {
158                         curl_formadd(&formpost,
159                                                  &lastptr,
160                                                  CURLFORM_COPYNAME, "album",
161                                                  CURLFORM_COPYCONTENTS, options.album,
162                                                  CURLFORM_END);
163                 }
164                 curl_formadd(&formpost,
165                              &lastptr,
166                              CURLFORM_COPYNAME, "access_type",
167                                          CURLFORM_COPYCONTENTS, access_type.c_str(),
168                              CURLFORM_END);
169                 if (title)
170                 curl_formadd(&formpost,
171                              &lastptr,
172                              CURLFORM_COPYNAME, "title",
173                                          CURLFORM_COPYCONTENTS, title,
174                              CURLFORM_END);
175                 if (tags)
176                 curl_formadd(&formpost,
177                              &lastptr,
178                              CURLFORM_COPYNAME, "tags",
179                                          CURLFORM_COPYCONTENTS, tags,
180                              CURLFORM_END);
181                 curl_formadd(&formpost,
182                              &lastptr,
183                              CURLFORM_COPYNAME, "pub_channel",
184                                          CURLFORM_COPYCONTENTS, PUB_CHANNEL,
185                              CURLFORM_END);
186                 curl_formadd(&formpost,
187                              &lastptr,
188                              CURLFORM_COPYNAME, "app_platform",
189                                          CURLFORM_COPYCONTENTS, APP_PLATFORM,
190                              CURLFORM_END);
191                 curl_formadd(&formpost,
192                              &lastptr,
193                              CURLFORM_COPYNAME, "app_version",
194                                          CURLFORM_COPYCONTENTS, APP_VERSION,
195                              CURLFORM_END);
196                 curl_formadd(&formpost,
197                              &lastptr,
198                              CURLFORM_COPYNAME, "yaru",
199                                          CURLFORM_COPYCONTENTS, publish.c_str(),
200                              CURLFORM_END);
201                 curl = curl_easy_init();
202                 if (curl) {
203                         headerlist = curl_slist_append(headerlist, expectHeader);
204                         char authHeader[2048];
205                         sprintf(authHeader,"Authorization: FimpToken realm=\"fotki.yandex.ru\", token=\"%s\"",token);
206                         headerlist = curl_slist_append(headerlist, authHeader);
207                         curl_easy_setopt(curl, CURLOPT_URL, "http://api-fotki.yandex.ru/post/");
208                         curl_easy_setopt(curl, CURLOPT_USERAGENT, PLUGIN_USER_AGENT);
209                         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
210                         curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
211                         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, getUrlContentWriteFunction);
212                         curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body);
213                         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
214                         curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curlProgressCallback);
215                         curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, transfer);
216                         res = curl_easy_perform(curl);
217                         curl_easy_cleanup(curl);
218                         if (CURLE_OK == res) ret = YANDEX_SEND_PHOTO_SUCCESS;
219                         else ret = YANDEX_SEND_PHOTO_FILE_INVALID_TOKEN; // TODO: check other errors too
220                 }
221                 curl_formfree(formpost);
222                 curl_slist_free_all (headerlist);
223         } else ret = YANDEX_SEND_PHOTO_FILE_NOT_FOUND;
224
225         if (tags) g_free(tags);
226         if (contentType) g_free(contentType);
227         if (title) g_free(title);
228         if (filename) g_free(filename);
229
230         return ret;
231 }
232
233 yandexGetAlbumsListResult yandexGetAlbumsList(const char* token, const char* username, GSList** albumOptionValues) {
234         yandexGetAlbumsListResult ret = YANDEX_GET_ALBUM_LIST_FAILED;
235
236         std::string albumsListURL("http://api-fotki.yandex.ru/api/users/");
237         albumsListURL += username; albumsListURL += "/albums/";
238         std::string body = getUrlContent(albumsListURL.c_str(),token);
239         if (!body.empty()) {
240                 xmlDocPtr xmlDoc = NULL;
241                 xmlDoc = xmlReadMemory(body.c_str(), body.length(), "albums.xml", NULL, 0);
242                 if (xmlDoc != NULL) {
243                         xmlNodePtr feed = xmlDoc->children;
244                         if (feed && (xmlStrEqual(feed->name, (const xmlChar*)"feed"))) {
245                                 xmlNodePtr entry = feed->children;
246                                 while (entry) {
247                                         if (xmlStrEqual(entry->name,(const xmlChar*)"entry")) {
248                                                 xmlChar* path = xmlGetNodePath(entry);
249                                                 if (path) free(path);
250                                                 xmlNodePtr id = NULL;
251                                                 xmlNodePtr title = NULL;
252                                                 xmlNodePtr summary = NULL;
253                                                 xmlNodePtr isProtected = NULL;
254                                                 xmlNodePtr entryChildren = entry->children;
255                                                 while (entryChildren) {
256                                                         if              (xmlStrEqual(entryChildren->name,(const xmlChar*)"id")) id = entryChildren;
257                                                         else if (xmlStrEqual(entryChildren->name,(const xmlChar*)"title")) title = entryChildren;
258                                                         else if (xmlStrEqual(entryChildren->name,(const xmlChar*)"summary")) summary = entryChildren;
259                                                         else if (xmlStrEqual(entryChildren->name,(const xmlChar*)"protected")) isProtected = entryChildren;
260                                                         entryChildren = entryChildren->next;
261                                                 }
262                                                 if (id) {
263                                                         xmlChar* idStr = xmlNodeGetContent(id);
264                                                         if (idStr) {
265                                                                 const xmlChar* idStrNum = NULL;
266                                                                 int s;
267                                                                 for (s=xmlStrlen(idStr);s>0;s--)
268                                                                         if (idStr[s] == ':') {
269                                                                                 idStrNum = idStr+s+1;
270                                                                                 break;
271                                                                         }
272                                                                 xmlChar* titleStr = title ? xmlNodeGetContent(title) : NULL;
273                                                                 xmlChar* summaryStr = summary ? xmlNodeGetContent(summary) : NULL;
274                                                                 xmlChar* isProtectedStr = isProtected ? xmlGetProp(isProtected,(const xmlChar*)"value") : NULL;
275
276                                                                 SharingServiceOptionValue* albumValue = sharing_service_option_value_new((const gchar*)idStrNum,titleStr?(const gchar*)titleStr:"Unnamed",summaryStr?(const gchar*)summaryStr:"");
277                                                                 *albumOptionValues = g_slist_append(*albumOptionValues,albumValue);
278                                                                 ret = YANDEX_GET_ALBUM_LIST_SUCCESS;
279
280                                                                 if (idStr) free(idStr);
281                                                                 if (titleStr) free(titleStr);
282                                                                 if (summaryStr) free(summaryStr);
283                                                                 if (isProtectedStr) free(isProtectedStr);
284                                                         }
285                                                 }
286                                         }
287                                         entry = entry->next;
288                                 }
289                         }
290                         xmlFreeDoc(xmlDoc);
291                 }
292         }
293
294         return ret;
295 }
296
297 static size_t getUrlContentWriteFunction(void *ptr, size_t size, size_t nmemb, void *data) {
298         std::string* body = (std::string*) data;
299         size_t realsize = size * nmemb;
300         if (body) body->append((const char*)ptr,realsize);
301         return realsize;
302 }
303 std::string getUrlContent(const char* url, const char* token) {
304         CURL *curl;
305         CURLcode res;
306         std::string body;
307         struct curl_slist *headerlist = NULL;
308         curl = curl_easy_init();
309         if(curl) {
310                 if (token) {
311                         std::string authHeader("Authorization: FimpToken realm=\"fotki.yandex.ru\", token=\"");
312                         authHeader += token; authHeader += "\"";
313                         headerlist = curl_slist_append(headerlist, authHeader.c_str());
314                 }
315                 curl_easy_setopt(curl, CURLOPT_URL, url);
316                 curl_easy_setopt(curl, CURLOPT_USERAGENT, PLUGIN_USER_AGENT);
317                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, getUrlContentWriteFunction);
318                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body);
319                 res = curl_easy_perform(curl);
320                 curl_easy_cleanup(curl);
321                 if (headerlist) curl_slist_free_all (headerlist);
322                 if (CURLE_OK != res) body.clear();
323         }
324         return body;
325 }
326
327 std::string getXmlElementValueByXPath(xmlDocPtr xmlDoc, const char* xmlXPath) {
328         std::string content;
329         if (xmlDoc != NULL) {
330                 xmlXPathContextPtr context = xmlXPathNewContext(xmlDoc);
331                 if (context != NULL) {
332                         xmlXPathObjectPtr result = xmlXPathEvalExpression((const xmlChar*)xmlXPath, context);
333                         if (result != NULL) {
334                                 if(!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
335                                         content = (char*) xmlNodeGetContent(result->nodesetval->nodeTab[0]);
336                                 }
337                                 xmlXPathFreeObject(result);
338                         }
339                         xmlXPathFreeContext(context);
340                 }
341         }
342         return content;
343 }
344
345 static gchar* createTagsStr (const GSList* tags) {
346    gchar* ret = NULL;
347    for (const GSList* p = tags; p != NULL; p = g_slist_next (p)) {
348        SharingTag* tag = (SharingTag*)(p->data);
349        SharingTagType type = sharing_tag_get_type(tag);
350        if (SHARING_TAG_SHARE != type) continue;
351        const gchar* tmp = sharing_tag_get_word (tag);
352        if (tmp != NULL) {
353            gchar* new_ret = NULL;
354            if (ret != NULL) {
355                new_ret = g_strdup_printf ("%s, %s", ret, tmp);
356                g_free (ret); /* old return is freed */
357            } else {
358                new_ret = g_strdup (tmp);
359            }
360            ret = new_ret;
361        }
362    }
363    return ret;
364 }