handle responses other than OK returned by audioscrobbler
[scrobby.git] / src / scrobby.cpp
blob1f8078c7a4f5a0f841f73111c5bc8979f95bda70
1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <csignal>
22 #include <cstdlib>
23 #include <curl/curl.h>
24 #include <iostream>
26 #include "callback.h"
27 #include "configuration.h"
28 #include "misc.h"
29 #include "scrobby.h"
30 #include "song.h"
31 #include "mpdpp.h"
33 using std::string;
35 Handshake myHandshake;
36 MPD::Song s;
38 pthread_mutex_t Handshake::itsLock = PTHREAD_MUTEX_INITIALIZER;
40 namespace
42 time_t now = 0;
44 void do_at_exit();
45 void signal_handler(int);
47 void *queue_handler(void *);
49 bool handshake_sent_properly();
52 int main(int argc, char **argv)
54 DefaultConfiguration(Config);
56 if (argc > 1)
58 ParseArgv(Config, argc, argv);
60 if (!Config.file_config.empty())
62 if (!ReadConfiguration(Config, Config.file_config))
64 std::cerr << "cannot read configuration file: " << Config.file_config << std::endl;
65 return 1;
68 else if (!ReadConfiguration(Config, Config.user_home_folder + "/.scrobbyconf"))
70 if (!ReadConfiguration(Config, "/etc/scrobby.conf"))
72 std::cerr << "default configuration files not found!\n";
73 return 1;
76 if (Config.log_level == llUndefined)
78 Config.log_level = llInfo;
80 if (Config.lastfm_user.empty() || (Config.lastfm_md5_password.empty() && Config.lastfm_password.empty()))
82 std::cerr << "last.fm user/password is not set.\n";
83 return 1;
85 ChangeToUser();
86 if (!CheckFiles(Config))
88 return 1;
90 if (Config.daemonize)
92 if (!Daemonize())
93 std::cerr << "couldn't daemonize!\n";
96 MPD::Song::GetCached();
98 MPD::Connection *Mpd = new MPD::Connection;
100 if (Config.mpd_host != "localhost")
101 Mpd->SetHostname(Config.mpd_host);
102 if (Config.mpd_port != 6600)
103 Mpd->SetPort(Config.mpd_port);
105 Mpd->SetTimeout(Config.mpd_timeout);
106 Mpd->SetStatusUpdater(ScrobbyStatusChanged, NULL);
107 Mpd->SetErrorHandler(ScrobbyErrorCallback, NULL);
109 signal(SIGHUP, signal_handler);
110 signal(SIGINT, signal_handler);
111 signal(SIGTERM, signal_handler);
112 signal(SIGPIPE, SIG_IGN);
114 atexit(do_at_exit);
116 curl_global_init(CURL_GLOBAL_ALL);
118 pthread_t queue_thread;
119 pthread_create(&queue_thread, 0, queue_handler, 0);
120 pthread_detach(queue_thread);
122 int handshake_delay = 0;
123 int mpd_delay = 0;
125 time_t handshake_ts = 0;
126 time_t mpd_ts = 0;
128 while (!sleep(1))
130 time(&now);
131 myHandshake.Lock();
132 if (now > handshake_ts && !myHandshake.OK())
134 myHandshake.Clear();
135 if (handshake_sent_properly() && !myHandshake.Status.empty())
137 Log(llInfo, "Handshake returned %s", myHandshake.Status.c_str());
139 if (!myHandshake.OK())
141 Log(llInfo, "Connected to Audioscrobbler!");
142 handshake_delay = 0;
144 else
146 handshake_delay += 20;
147 Log(llInfo, "Connection to Audioscrobbler refused, retrieving in %d seconds...", handshake_delay);
148 handshake_ts = time(0)+handshake_delay;
151 myHandshake.Unlock();
152 if (Mpd->Connected())
154 Mpd->UpdateStatus();
156 else if (now > mpd_ts)
158 s.Submit();
159 Log(llVerbose, "Connecting to MPD...");
160 if (Mpd->Connect())
162 Log(llInfo, "Connected to MPD at %s !", Config.mpd_host.c_str());
163 mpd_delay = 0;
165 else
167 mpd_delay += 10;
168 Log(llInfo, "Cannot connect to MPD, retrieving in %d seconds...", mpd_delay);
169 mpd_ts = time(0)+mpd_delay;
173 return 0;
176 namespace
178 void do_at_exit()
180 s.Submit();
181 s.ExtractQueue();
182 Log(llInfo, "Shutting down...");
183 if (remove(Config.file_pid.c_str()) != 0)
184 Log(llInfo, "Couldn't remove pid file!");
187 void signal_handler(int)
189 exit(0);
192 void *queue_handler(void *)
194 int queue_delay = 0;
195 time_t queue_ts = 0;
196 while (!sleep(1))
198 if (now > queue_ts && (!MPD::Song::SubmitQueue.empty() || !MPD::Song::Queue.empty()))
200 if (!MPD::Song::SendQueue())
202 queue_delay += 30;
203 Log(llInfo, "Submission failed, retrieving in %d seconds...", queue_delay);
204 queue_ts = time(0)+queue_delay;
206 else
207 queue_delay = 0;
210 pthread_exit(0);
213 bool handshake_sent_properly()
215 CURLcode code;
216 string handshake_url;
217 string result;
218 string timestamp = IntoStr(time(NULL));
220 handshake_url = "http://post.audioscrobbler.com/?hs=true&p=1.2.1&c=mpc&v="VERSION"&u=";
221 handshake_url += Config.lastfm_user;
222 handshake_url += "&t=";
223 handshake_url += timestamp;
224 handshake_url += "&a=";
225 handshake_url += md5sum((Config.lastfm_md5_password.empty() ? md5sum(Config.lastfm_password) : Config.lastfm_md5_password) + timestamp);
227 CURL *hs = curl_easy_init();
228 curl_easy_setopt(hs, CURLOPT_URL, handshake_url.c_str());
229 curl_easy_setopt(hs, CURLOPT_WRITEFUNCTION, write_data);
230 curl_easy_setopt(hs, CURLOPT_WRITEDATA, &result);
231 curl_easy_setopt(hs, CURLOPT_CONNECTTIMEOUT, curl_connecttimeout);
232 curl_easy_setopt(hs, CURLOPT_TIMEOUT, curl_timeout);
233 curl_easy_setopt(hs, CURLOPT_DNS_CACHE_TIMEOUT, 0);
234 curl_easy_setopt(hs, CURLOPT_NOPROGRESS, 1);
235 curl_easy_setopt(hs, CURLOPT_NOSIGNAL, 1);
236 code = curl_easy_perform(hs);
237 curl_easy_cleanup(hs);
239 if (code != CURLE_OK)
241 Log(llInfo, "Error while sending handshake: %s", curl_easy_strerror(code));
242 return false;
245 size_t i = result.find("\n");
246 myHandshake.Status = result.substr(0, i);
247 if (myHandshake.Status != "OK")
249 if (myHandshake.Status == "BANNED")
251 Log(llInfo, "Ops, this version of scrobby is banned. Please update to the newest one or if it's the newest, inform me about it (electricityispower@gmail.com)");
253 else if (myHandshake.Status == "BADAUTH")
255 Log(llInfo, "User authentication failed. Please recheck your username/password settings.");
257 else
258 return false;
259 exit(1);
261 result = result.substr(i+1);
262 i = result.find("\n");
263 myHandshake.SessionID = result.substr(0, i);
264 result = result.substr(i+1);
265 i = result.find("\n");
266 myHandshake.NowPlayingURL = result.substr(0, i);
267 result = result.substr(i+1);
268 IgnoreNewlines(result);
269 myHandshake.SubmissionURL = result;
270 return true;