get rid of threads
[scrobby.git] / src / scrobby.cpp
blob2d6cdefdd393b12294df87ae7a9bd09a42434637
1 /***************************************************************************
2 * Copyright (C) 2008 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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <csignal>
22 #include <cstdlib>
23 #include <curl/curl.h>
24 #include <iostream>
25 #include <vector>
27 #include "callback.h"
28 #include "configuration.h"
29 #include "misc.h"
30 #include "scrobby.h"
31 #include "song.h"
32 #include "mpdpp.h"
34 using std::string;
36 ScrobbyConfig config;
38 Handshake handshake;
39 MPD::Song s;
41 std::vector<string> SongsQueue;
43 bool notify_about_now_playing = 0;
45 namespace {
46 void do_at_exit();
48 void signal_handler(int);
49 bool handshake_sent_properly();
51 void *handshake_handler(void *);
54 int main(int argc, char **argv)
56 DefaultConfiguration(config);
58 if (argc > 1)
60 ParseArgv(config, argc, argv);
62 if (!config.file_config.empty())
64 if (!ReadConfiguration(config, config.file_config))
66 std::cerr << "cannot read configuration file: " << config.file_config << std::endl;
67 return 1;
70 else if (!ReadConfiguration(config, config.user_home_folder + "/.scrobbyconf"))
72 if (!ReadConfiguration(config, "/etc/scrobby.conf"))
74 std::cerr << "default configuration files not found!\n";
75 return 1;
78 if (config.log_level == llUndefined)
80 config.log_level = llInfo;
82 if (config.lastfm_user.empty() || (config.lastfm_md5_password.empty() && config.lastfm_password.empty()))
84 std::cerr << "last.fm user/password is not set.\n";
85 return 1;
87 ChangeToUser();
88 if (!CheckFiles(config))
90 return 1;
92 if (config.daemonize)
94 if (!Daemonize())
95 std::cerr << "couldn't daemonize!\n";
98 GetCachedSongs(SongsQueue);
100 MPD::Connection *Mpd = new MPD::Connection;
102 if (config.mpd_host != "localhost")
103 Mpd->SetHostname(config.mpd_host);
104 if (config.mpd_port != 6600)
105 Mpd->SetPort(config.mpd_port);
107 Mpd->SetTimeout(config.mpd_timeout);
108 Mpd->SetStatusUpdater(ScrobbyStatusChanged, NULL);
109 Mpd->SetErrorHandler(ScrobbyErrorCallback, NULL);
111 signal(SIGHUP, signal_handler);
112 signal(SIGINT, signal_handler);
113 signal(SIGTERM, signal_handler);
114 signal(SIGPIPE, SIG_IGN);
116 atexit(do_at_exit);
118 int handshake_delay = 0;
119 int mpd_delay = 0;
121 time_t now = 0;
122 time_t handshake_ts = 0;
123 time_t mpd_ts = 0;
125 while (!usleep(500000))
127 time(&now);
128 if (now > handshake_delay && handshake.status != "OK")
130 handshake.Clear();
131 if (handshake_sent_properly() && !handshake.status.empty())
133 Log(llInfo, "Handshake returned %s", handshake.status.c_str());
135 if (handshake.status == "OK")
137 Log(llInfo, "Connected to Audioscrobbler!");
138 if (!SongsQueue.empty())
140 Log(llInfo, "Queue's not empty, submitting songs...");
142 string result, postdata;
143 CURLcode code;
145 CURL *submission = curl_easy_init();
147 postdata = "s=";
148 postdata += handshake.session_id;
150 for (std::vector<string>::const_iterator it = SongsQueue.begin(); it != SongsQueue.end(); it++)
151 postdata += *it;
153 Log(llVerbose, "URL: %s", handshake.submission_url.c_str());
154 Log(llVerbose, "Post data: %s", postdata.c_str());
156 curl_easy_setopt(submission, CURLOPT_URL, handshake.submission_url.c_str());
157 curl_easy_setopt(submission, CURLOPT_POST, 1);
158 curl_easy_setopt(submission, CURLOPT_POSTFIELDS, postdata.c_str());
159 curl_easy_setopt(submission, CURLOPT_WRITEFUNCTION, write_data);
160 curl_easy_setopt(submission, CURLOPT_WRITEDATA, &result);
161 curl_easy_setopt(submission, CURLOPT_CONNECTTIMEOUT, curl_timeout);
162 curl_easy_setopt(submission, CURLOPT_NOSIGNAL, 1);
163 code = curl_easy_perform(submission);
164 curl_easy_cleanup(submission);
166 ignore_newlines(result);
168 if (result == "OK")
170 Log(llInfo, "Number of submitted songs: %d", SongsQueue.size());
171 SongsQueue.clear();
172 ClearCache();
173 handshake_delay = 0;
175 else
177 if (result.empty())
179 Log(llInfo, "Error while submitting songs: %s", curl_easy_strerror(code));
181 else
183 Log(llInfo, "Audioscrobbler returned status %s", result.c_str());
187 notify_about_now_playing = !s.isStream();
189 else
191 handshake_delay += 10;
192 Log(llInfo, "Connection to Audioscrobbler refused, retrieving in %d seconds...", handshake_delay);
193 handshake_ts = time(0)+handshake_delay;
196 if (Mpd->Connected())
198 Mpd->UpdateStatus();
200 else if (now > mpd_ts)
202 s.Submit();
203 Log(llVerbose, "Connecting to MPD...");
204 if (Mpd->Connect())
206 Log(llInfo, "Connected to MPD at %s !", config.mpd_host.c_str());
207 mpd_delay = 0;
209 else
211 mpd_delay += 10;
212 Log(llInfo, "Cannot connect to MPD, retrieving in %d seconds...", mpd_delay);
213 mpd_ts = time(0)+mpd_delay;
217 return 0;
220 namespace {
222 void do_at_exit()
224 s.Submit();
225 Log(llInfo, "Shutting down...");
226 if (remove(config.file_pid.c_str()) != 0)
227 Log(llInfo, "Couldn't remove pid file!");
230 void signal_handler(int)
232 exit(0);
235 bool handshake_sent_properly()
237 CURLcode code;
238 string handshake_url;
239 string result;
240 string timestamp = IntoStr(time(NULL));
242 handshake_url = "http://post.audioscrobbler.com/?hs=true&p=1.2.1&c=mpc&v="VERSION"&u=";
243 handshake_url += config.lastfm_user;
244 handshake_url += "&t=";
245 handshake_url += timestamp;
246 handshake_url += "&a=";
247 handshake_url += md5sum((config.lastfm_md5_password.empty() ? md5sum(config.lastfm_password) : config.lastfm_md5_password) + timestamp);
249 CURL *hs = curl_easy_init();
250 curl_easy_setopt(hs, CURLOPT_URL, handshake_url.c_str());
251 curl_easy_setopt(hs, CURLOPT_WRITEFUNCTION, write_data);
252 curl_easy_setopt(hs, CURLOPT_WRITEDATA, &result);
253 curl_easy_setopt(hs, CURLOPT_CONNECTTIMEOUT, curl_timeout);
254 curl_easy_setopt(hs, CURLOPT_NOSIGNAL, 1);
255 code = curl_easy_perform(hs);
256 curl_easy_cleanup(hs);
258 if (code != CURLE_OK)
260 Log(llInfo, "Error while sending handshake: %s", curl_easy_strerror(code));
261 return false;
264 size_t i = result.find("\n");
265 handshake.status = result.substr(0, i);
266 if (handshake.status != "OK")
267 return false;
268 result = result.substr(i+1);
269 i = result.find("\n");
270 handshake.session_id = result.substr(0, i);
271 result = result.substr(i+1);
272 i = result.find("\n");
273 handshake.nowplaying_url = result.substr(0, i);
274 result = result.substr(i+1);
275 ignore_newlines(result);
276 handshake.submission_url = result;
277 return true;