1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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 ***************************************************************************/
23 #include <curl/curl.h>
27 #include "configuration.h"
35 Handshake myHandshake
;
38 pthread_mutex_t
Handshake::itsLock
= PTHREAD_MUTEX_INITIALIZER
;
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
);
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
;
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";
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";
86 if (!CheckFiles(Config
))
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
);
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;
125 time_t handshake_ts
= 0;
132 if (now
> handshake_ts
&& !myHandshake
.OK())
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!");
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())
156 else if (now
> mpd_ts
)
159 Log(llVerbose
, "Connecting to MPD...");
162 Log(llInfo
, "Connected to MPD at %s !", Config
.mpd_host
.c_str());
168 Log(llInfo
, "Cannot connect to MPD, retrieving in %d seconds...", mpd_delay
);
169 mpd_ts
= time(0)+mpd_delay
;
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)
192 void *queue_handler(void *)
198 if (now
> queue_ts
&& (!MPD::Song::SubmitQueue
.empty() || !MPD::Song::Queue
.empty()))
200 if (!MPD::Song::SendQueue())
203 Log(llInfo
, "Submission failed, retrieving in %d seconds...", queue_delay
);
204 queue_ts
= time(0)+queue_delay
;
213 bool handshake_sent_properly()
216 string handshake_url
;
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
));
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.");
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
;