2 * Copyright (C) 2013-2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
3 * Copyright (C) 2015-2016 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "ardour/debug.h"
21 #include "ardour/soundcloud_upload.h"
23 #include "pbd/xml++.h"
24 #include <pbd/error.h>
27 #include <sys/types.h>
29 #include "pbd/gstdio_compat.h"
36 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
38 register int realsize = (int)(size * nmemb);
39 struct MemoryStruct *mem = (struct MemoryStruct *)data;
41 mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
44 memcpy(&(mem->memory[mem->size]), ptr, realsize);
45 mem->size += realsize;
46 mem->memory[mem->size] = 0;
51 SoundcloudUploader::SoundcloudUploader()
55 curl_handle = curl_easy_init();
56 multi_handle = curl_multi_init();
60 SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
62 struct MemoryStruct xml_page;
63 xml_page.memory = NULL;
68 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
69 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
71 struct curl_httppost *formpost=NULL;
72 struct curl_httppost *lastptr=NULL;
74 /* Fill in the filename field */
75 curl_formadd(&formpost,
77 CURLFORM_COPYNAME, "client_id",
78 CURLFORM_COPYCONTENTS, "6dd9cf0ad281aa57e07745082cec580b",
81 curl_formadd(&formpost,
83 CURLFORM_COPYNAME, "client_secret",
84 CURLFORM_COPYCONTENTS, "53f5b0113fb338800f8a7a9904fc3569",
87 curl_formadd(&formpost,
89 CURLFORM_COPYNAME, "grant_type",
90 CURLFORM_COPYCONTENTS, "password",
93 curl_formadd(&formpost,
95 CURLFORM_COPYNAME, "username",
96 CURLFORM_COPYCONTENTS, username.c_str(),
99 curl_formadd(&formpost,
101 CURLFORM_COPYNAME, "password",
102 CURLFORM_COPYCONTENTS, password.c_str(),
105 struct curl_slist *headerlist=NULL;
106 headerlist = curl_slist_append(headerlist, "Expect:");
107 headerlist = curl_slist_append(headerlist, "Accept: application/xml");
108 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
110 /* what URL that receives this POST */
111 std::string url = "https://api.soundcloud.com/oauth2/token";
112 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
113 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
115 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
117 // perform online request
118 CURLcode res = curl_easy_perform(curl_handle);
120 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("curl error %1 (%2)\n", res, curl_easy_strerror(res) ) );
124 if (xml_page.memory){
125 // cheesy way to parse the json return value. find access_token, then advance 3 quotes
127 if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
128 error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
132 std::string token = strtok( xml_page.memory, "access_token" );
133 token = strtok( NULL, "\"" );
134 token = strtok( NULL, "\"" );
135 token = strtok( NULL, "\"" );
137 free( xml_page.memory );
145 SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
147 SoundcloudUploader *scu = (SoundcloudUploader *) caller;
148 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("%1: uploaded %2 of %3\n", scu->title, ulnow, ultotal) );
149 scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
155 SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
159 struct MemoryStruct xml_page;
160 xml_page.memory = NULL;
165 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
166 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
168 struct curl_httppost *formpost=NULL;
169 struct curl_httppost *lastptr=NULL;
171 /* Fill in the file upload field. This makes libcurl load data from
172 the given file name when curl_easy_perform() is called. */
173 curl_formadd(&formpost,
175 CURLFORM_COPYNAME, "track[asset_data]",
176 CURLFORM_FILE, file_path.c_str(),
179 /* Fill in the filename field */
180 curl_formadd(&formpost,
182 CURLFORM_COPYNAME, "oauth_token",
183 CURLFORM_COPYCONTENTS, token.c_str(),
186 curl_formadd(&formpost,
188 CURLFORM_COPYNAME, "track[title]",
189 CURLFORM_COPYCONTENTS, title.c_str(),
192 curl_formadd(&formpost,
194 CURLFORM_COPYNAME, "track[sharing]",
195 CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
198 curl_formadd(&formpost,
200 CURLFORM_COPYNAME, "track[downloadable]",
201 CURLFORM_COPYCONTENTS, downloadable ? "true" : "false",
206 /* initalize custom header list (stating that Expect: 100-continue is not
208 struct curl_slist *headerlist=NULL;
209 static const char buf[] = "Expect:";
210 headerlist = curl_slist_append(headerlist, buf);
213 if (curl_handle && multi_handle) {
215 /* what URL that receives this POST */
216 std::string url = "https://api.soundcloud.com/tracks";
217 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
218 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
220 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
221 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
223 this->title = title; // save title to show in progress bar
224 this->caller = caller;
226 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
227 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
228 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
230 curl_multi_add_handle(multi_handle, curl_handle);
232 curl_multi_perform(multi_handle, &still_running);
235 while(still_running) {
236 struct timeval timeout;
237 int rc; /* select() return code */
244 long curl_timeo = -1;
250 /* set a suitable timeout to play around with */
254 curl_multi_timeout(multi_handle, &curl_timeo);
255 if(curl_timeo >= 0) {
256 timeout.tv_sec = curl_timeo / 1000;
257 if(timeout.tv_sec > 1)
260 timeout.tv_usec = (curl_timeo % 1000) * 1000;
263 /* get file descriptors from the transfers */
264 curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
266 /* In a real-world program you OF COURSE check the return code of the
267 function calls. On success, the value of maxfd is guaranteed to be
268 greater or equal than -1. We call select(maxfd + 1, ...), specially in
269 case of (maxfd == -1), we call select(0, ...), which is basically equal
272 rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
280 /* timeout or readable/writable sockets */
281 curl_multi_perform(multi_handle, &still_running);
286 /* then cleanup the formpost chain */
287 curl_formfree(formpost);
290 curl_slist_free_all (headerlist);
293 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
297 DEBUG_TRACE (DEBUG::Soundcloud, xml_page.memory);
300 doc.read_buffer( xml_page.memory );
301 XMLNode *root = doc.root();
304 DEBUG_TRACE (DEBUG::Soundcloud, "no root XML node!\n");
308 XMLNode *url_node = root->child("permalink-url");
310 DEBUG_TRACE (DEBUG::Soundcloud, "no child node \"permalink-url\" found!\n");
314 XMLNode *text_node = url_node->child("text");
316 DEBUG_TRACE (DEBUG::Soundcloud, "no text node found!\n");
320 free( xml_page.memory );
321 return text_node->content();
328 SoundcloudUploader:: ~SoundcloudUploader()
330 curl_easy_cleanup(curl_handle);
331 curl_multi_cleanup(multi_handle);
336 SoundcloudUploader::setcUrlOptions()
338 // some servers don't like requests that are made without a user-agent field, so we provide one
339 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
340 // setup curl error buffer
341 curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
343 curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
345 // Allow connections to time out (without using signals)
346 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
347 curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
349 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
350 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);