fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / soundcloud_upload.cc
1 /* soundcloud_export.cpp **********************************************************************
2
3         Adapted for Ardour by Ben Loftis, March 2012
4
5         Licence GPL:
6
7         This program is free software; you can redistribute it and/or
8         modify it under the terms of the GNU General Public License
9         as published by the Free Software Foundation; either version 2
10         of the License, or (at your option) any later version.
11
12         This program is distributed in the hope that it will be useful,
13         but WITHOUT ANY WARRANTY; without even the implied warranty of
14         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15         GNU General Public License for more details.
16
17         You should have received a copy of the GNU General Public License
18         along with this program; if not, write to the Free Software
19         Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21
22 *************************************************************************************/
23 #include "ardour/debug.h"
24 #include "ardour/soundcloud_upload.h"
25
26 #include "pbd/xml++.h"
27 #include <pbd/error.h>
28
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <iostream>
32 #include "pbd/gstdio_compat.h"
33
34 #include "pbd/i18n.h"
35
36 using namespace PBD;
37
38 size_t
39 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
40 {
41         register int realsize = (int)(size * nmemb);
42         struct MemoryStruct *mem = (struct MemoryStruct *)data;
43
44         mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
45
46         if (mem->memory) {
47                 memcpy(&(mem->memory[mem->size]), ptr, realsize);
48                 mem->size += realsize;
49                 mem->memory[mem->size] = 0;
50         }
51         return realsize;
52 }
53
54 SoundcloudUploader::SoundcloudUploader()
55 {
56         curl_handle = curl_easy_init();
57         multi_handle = curl_multi_init();
58 }
59
60 std::string
61 SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
62 {
63         struct MemoryStruct xml_page;
64         xml_page.memory = NULL;
65         xml_page.size = 0;
66
67         setcUrlOptions();
68
69         curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
70         curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
71
72         struct curl_httppost *formpost=NULL;
73         struct curl_httppost *lastptr=NULL;
74
75         /* Fill in the filename field */
76         curl_formadd(&formpost,
77                         &lastptr,
78                         CURLFORM_COPYNAME, "client_id",
79                         CURLFORM_COPYCONTENTS, "6dd9cf0ad281aa57e07745082cec580b",
80                         CURLFORM_END);
81
82         curl_formadd(&formpost,
83                         &lastptr,
84                         CURLFORM_COPYNAME, "client_secret",
85                         CURLFORM_COPYCONTENTS, "53f5b0113fb338800f8a7a9904fc3569",
86                         CURLFORM_END);
87
88         curl_formadd(&formpost,
89                         &lastptr,
90                         CURLFORM_COPYNAME, "grant_type",
91                         CURLFORM_COPYCONTENTS, "password",
92                         CURLFORM_END);
93
94         curl_formadd(&formpost,
95                         &lastptr,
96                         CURLFORM_COPYNAME, "username",
97                         CURLFORM_COPYCONTENTS, username.c_str(),
98                         CURLFORM_END);
99
100         curl_formadd(&formpost,
101                         &lastptr,
102                         CURLFORM_COPYNAME, "password",
103                         CURLFORM_COPYCONTENTS, password.c_str(),
104                         CURLFORM_END);
105
106         struct curl_slist *headerlist=NULL;
107         headerlist = curl_slist_append(headerlist, "Expect:");
108         headerlist = curl_slist_append(headerlist, "Accept: application/xml");
109         curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
110
111         /* what URL that receives this POST */
112         std::string url = "https://api.soundcloud.com/oauth2/token";
113         curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
114         curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
115
116         // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
117
118         // perform online request
119         CURLcode res = curl_easy_perform(curl_handle);
120         if (res != 0) {
121                 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("curl error %1 (%2)", res, curl_easy_strerror(res) ) );
122                 return "";
123         }
124
125         if (xml_page.memory){
126                 // cheesy way to parse the json return value.  find access_token, then advance 3 quotes
127
128                 if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
129                         error << _("Upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
130                         return "";
131                 }
132
133                 std::string token = strtok( xml_page.memory, "access_token" );
134                 token = strtok( NULL, "\"" );
135                 token = strtok( NULL, "\"" );
136                 token = strtok( NULL, "\"" );
137
138                 free( xml_page.memory );
139                 return token;
140         }
141
142         return "";
143 }
144
145 int
146 SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
147 {
148         SoundcloudUploader *scu = (SoundcloudUploader *) caller;
149         DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("%1: uploaded %2 of %3", scu->title, ulnow, ultotal) );
150         scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
151         return 0;
152 }
153
154
155 std::string
156 SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
157 {
158         int still_running;
159
160         struct MemoryStruct xml_page;
161         xml_page.memory = NULL;
162         xml_page.size = 0;
163
164         setcUrlOptions();
165
166         curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
167         curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
168
169         struct curl_httppost *formpost=NULL;
170         struct curl_httppost *lastptr=NULL;
171
172         /* Fill in the file upload field. This makes libcurl load data from
173            the given file name when curl_easy_perform() is called. */
174         curl_formadd(&formpost,
175                         &lastptr,
176                         CURLFORM_COPYNAME, "track[asset_data]",
177                         CURLFORM_FILE, file_path.c_str(),
178                         CURLFORM_END);
179
180         /* Fill in the filename field */
181         curl_formadd(&formpost,
182                         &lastptr,
183                         CURLFORM_COPYNAME, "oauth_token",
184                         CURLFORM_COPYCONTENTS, token.c_str(),
185                         CURLFORM_END);
186
187         curl_formadd(&formpost,
188                         &lastptr,
189                         CURLFORM_COPYNAME, "track[title]",
190                         CURLFORM_COPYCONTENTS, title.c_str(),
191                         CURLFORM_END);
192
193         curl_formadd(&formpost,
194                         &lastptr,
195                         CURLFORM_COPYNAME, "track[sharing]",
196                         CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
197                         CURLFORM_END);
198
199         curl_formadd(&formpost,
200                         &lastptr,
201                         CURLFORM_COPYNAME, "track[downloadable]",
202                         CURLFORM_COPYCONTENTS, downloadable ? "true" : "false",
203                         CURLFORM_END);
204
205
206
207         /* initalize custom header list (stating that Expect: 100-continue is not
208            wanted */
209         struct curl_slist *headerlist=NULL;
210         static const char buf[] = "Expect:";
211         headerlist = curl_slist_append(headerlist, buf);
212
213
214         if (curl_handle && multi_handle) {
215
216                 /* what URL that receives this POST */
217                 std::string url = "https://api.soundcloud.com/tracks";
218                 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
219                 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
220
221                 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
222                 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
223
224                 this->title = title; // save title to show in progress bar
225                 this->caller = caller;
226
227                 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
228                 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
229                 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
230
231                 curl_multi_add_handle(multi_handle, curl_handle);
232
233                 curl_multi_perform(multi_handle, &still_running);
234
235
236                 while(still_running) {
237                         struct timeval timeout;
238                         int rc; /* select() return code */
239
240                         fd_set fdread;
241                         fd_set fdwrite;
242                         fd_set fdexcep;
243                         int maxfd = -1;
244
245                         long curl_timeo = -1;
246
247                         FD_ZERO(&fdread);
248                         FD_ZERO(&fdwrite);
249                         FD_ZERO(&fdexcep);
250
251                         /* set a suitable timeout to play around with */
252                         timeout.tv_sec = 1;
253                         timeout.tv_usec = 0;
254
255                         curl_multi_timeout(multi_handle, &curl_timeo);
256                         if(curl_timeo >= 0) {
257                                 timeout.tv_sec = curl_timeo / 1000;
258                                 if(timeout.tv_sec > 1)
259                                         timeout.tv_sec = 1;
260                                 else
261                                         timeout.tv_usec = (curl_timeo % 1000) * 1000;
262                         }
263
264                         /* get file descriptors from the transfers */
265                         curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
266
267                         /* In a real-world program you OF COURSE check the return code of the
268                            function calls.  On success, the value of maxfd is guaranteed to be
269                            greater or equal than -1.  We call select(maxfd + 1, ...), specially in
270                            case of (maxfd == -1), we call select(0, ...), which is basically equal
271                            to sleep. */
272
273                         rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
274
275                         switch(rc) {
276                                 case -1:
277                                         /* select error */
278                                         break;
279                                 case 0:
280                                 default:
281                                         /* timeout or readable/writable sockets */
282                                         curl_multi_perform(multi_handle, &still_running);
283                                         break;
284                         }
285                 }
286
287                 /* then cleanup the formpost chain */
288                 curl_formfree(formpost);
289
290                 /* free slist */
291                 curl_slist_free_all (headerlist);
292         }
293
294         curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
295
296         if(xml_page.memory){
297
298                 DEBUG_TRACE (DEBUG::Soundcloud, xml_page.memory);
299
300                 XMLTree doc;
301                 doc.read_buffer( xml_page.memory );
302                 XMLNode *root = doc.root();
303
304                 if (!root) {
305                         DEBUG_TRACE (DEBUG::Soundcloud, "no root XML node!");
306                         return "";
307                 }
308
309                 XMLNode *url_node = root->child("permalink-url");
310                 if (!url_node) {
311                         DEBUG_TRACE (DEBUG::Soundcloud, "no child node \"permalink-url\" found!");
312                         return "";
313                 }
314
315                 XMLNode *text_node = url_node->child("text");
316                 if (!text_node) {
317                         DEBUG_TRACE (DEBUG::Soundcloud, "no text node found!");
318                         return "";
319                 }
320
321                 free( xml_page.memory );
322                 return text_node->content();
323         }
324
325         return "";
326 };
327
328
329 SoundcloudUploader:: ~SoundcloudUploader()
330 {
331         curl_easy_cleanup(curl_handle);
332         curl_multi_cleanup(multi_handle);
333 }
334
335
336 void
337 SoundcloudUploader::setcUrlOptions()
338 {
339         // basic init for curl
340         curl_global_init(CURL_GLOBAL_ALL);
341         // some servers don't like requests that are made without a user-agent field, so we provide one
342         curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
343         // setup curl error buffer
344         curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
345         // Allow redirection
346         curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
347
348         // Allow connections to time out (without using signals)
349         curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
350         curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
351
352         curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
353         curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
354 }
355