ardour
soundcloud_upload.cc
Go to the documentation of this file.
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"
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 <glib/gstdio.h>
33 
34 #include "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 
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 
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 
330 {
331  curl_easy_cleanup(curl_handle);
332  curl_multi_cleanup(multi_handle);
333 }
334 
335 
336 void
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 
const std::string & content() const
Definition: xml++.h:107
LIBPBD_API Transmitter error
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
bool read_buffer(const std::string &)
Definition: xml++.cc:125
Definition: xml++.h:55
#define _(Text)
Definition: i18n.h:11
XMLNode * root() const
Definition: xml++.h:62
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
ARDOUR::ExportHandler * caller
std::string Get_Auth_Token(std::string username, std::string password)
size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
Definition: xml++.h:95
LIBARDOUR_API PBD::PropertyDescriptor< bool > select
Definition: route_group.cc:48
Definition: debug.h:30
XMLNode * child(const char *) const
Definition: xml++.cc:309
std::string Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
LIBARDOUR_API uint64_t Soundcloud
Definition: debug.cc:66
static int progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
PBD::Signal3< void, double, double, std::string > SoundcloudProgress