Esempio n. 1
0
/*	get session id from last.fm; you don't have to call this manually
 *	@param wardrobe handle
 *	@return _OK or error
 */
static WardrobeReturn_t WardrobeHandshake (WardrobeHandle_t *wh) {
	/* md5 hash length + long integer max + NULL */
	char url[WARDROBE_URL_SIZE], tmp[32+55+1], *tmpDigest, *pwDigest,
			*ret, postUrl[1024];
	WardrobeReturn_t fRet = WARDROBE_RET_ERR;
	time_t currTStamp = time (NULL);

	tmpDigest = WardrobeMd5Calc (wh->password);
	snprintf (tmp, sizeof (tmp), "%s%li", tmpDigest, currTStamp);
	pwDigest = WardrobeMd5Calc (tmp);
	snprintf (url, sizeof (url), "/?hs=true&p=1.2&c=pia&v=1.0&u=%s&t=%li&a=%s",
			wh->user, currTStamp, pwDigest);
	
	WaitressSetHPP (&wh->waith, "post.audioscrobbler.com", "80", url);
	wh->waith.method = WAITRESS_METHOD_GET;
	wh->waith.postData = NULL;
	wh->waith.extraHeaders = NULL;
	if (WaitressFetchBuf (&wh->waith, &ret) != WAITRESS_RET_OK) {
		return WARDROBE_RET_CONNECT_ERR;
	}

	/* parse answer */
	if (memcmp (ret, "OK", 2) == 0) {
		char *newlines[5];
		size_t i;
		newlines[0] = ret;
		/* split string */
		for (i = 1; i < sizeof (newlines) / sizeof (*newlines); i++) {
			newlines[i] = strchr (newlines[i-1]+1, '\n');
		}
		/* copy needed values (auth token and post url) */
		if (newlines[2] - newlines[1]-1 < sizeof (wh->authToken)) {
			memcpy (wh->authToken, newlines[1]+1, newlines[2] -
					newlines[1]-1);
		} else {
			printf ("buffer overflow!\n");
		}
		if (newlines[4] - newlines[3]-1 < sizeof (postUrl)) {
			memset (postUrl, 0, sizeof (postUrl));
			memcpy (postUrl, newlines[3]+1, newlines[4] -
					newlines[3]-1);
			WaitressSplitUrl (postUrl, wh->postHost, sizeof (wh->postHost),
					wh->postPort, sizeof (wh->postPort), wh->postPath,
					sizeof (wh->postPath));
		} else {
			printf ("buffer overflow!\n");
		}
		fRet = WARDROBE_RET_OK;
	} else if (memcmp (ret, "BADAUTH", 7) == 0) {
		fRet = WARDROBE_RET_BADAUTH;
	} else if (memcmp (ret, "BADTIME", 7) == 0) {
		fRet = WARDROBE_RET_BADTIME;
	} else if (memcmp (ret, "BANNED", 6) == 0) {
		fRet = WARDROBE_RET_CLIENT_BANNED;
	}

	WardrobeFree (tmpDigest, WARDROBE_MD5_DIGEST_LEN);
	WardrobeFree (pwDigest, WARDROBE_MD5_DIGEST_LEN);
	WardrobeFree (ret, 0);

	return fRet;
}
Esempio n. 2
0
int main (int argc, char **argv) {
	/* handles */
	PianoHandle_t ph;
	static struct audioPlayer player;
	BarSettings_t settings;
	pthread_t playerThread;
	/* playlist; first item is current song */
	PianoSong_t *playlist = NULL;
	PianoSong_t *songHistory = NULL;
	PianoStation_t *curStation = NULL;
	char doQuit = 0;
	/* FIXME: max path length? */
	char ctlPath[1024];
	FILE *ctlFd = NULL;
	struct timeval selectTimeout;
	int maxFd, selectFds[2];
	fd_set readSet, readSetCopy;
	char buf = '\0';
	/* terminal attributes _before_ we started messing around with ~ECHO */
	struct termios termOrig;

	BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "!\n");

	/* save terminal attributes, before disabling echoing */
	BarTermSave (&termOrig);

	BarTermSetEcho (0);
	BarTermSetBuffer (0);
	/* init some things */
	ao_initialize ();
	PianoInit (&ph);
	BarSettingsInit (&settings);
	BarSettingsRead (&settings);

	/* init fds */
	FD_ZERO(&readSet);
	selectFds[0] = fileno (stdin);
	FD_SET(selectFds[0], &readSet);
	maxFd = selectFds[0] + 1;

	BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath));
	/* FIXME: why is r_+_ required? */
	ctlFd = fopen (ctlPath, "r+");
	if (ctlFd != NULL) {
		selectFds[1] = fileno (ctlFd);
		FD_SET(selectFds[1], &readSet);
		/* assuming ctlFd is always > stdin */
		maxFd = selectFds[1] + 1;
		BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath);
	}

	if (settings.username == NULL) {
		char nameBuf[100];
		BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... ");
	if (BarUiPrintPianoStatus (PianoConnect (&ph, settings.username,
			settings.password)) !=
			PIANO_RET_OK) {
		BarTermRestore (&termOrig);
		return 0;
	}
	BarUiMsg (MSG_INFO, "Get stations... ");
	if (BarUiPrintPianoStatus (PianoGetStations (&ph)) != PIANO_RET_OK) {
		BarTermRestore (&termOrig);
		return 0;
	}

	/* try to get autostart station */
	if (settings.autostartStation != NULL) {
		curStation = PianoFindStationById (ph.stations,
				settings.autostartStation);
		if (curStation == NULL) {
			BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n");
		}
	}
	/* no autostart? ask the user */
	if (curStation == NULL) {
		curStation = BarUiSelectStation (&ph, "Select station: ", stdin);
	}
	if (curStation != NULL) {
		BarUiPrintStation (curStation);
	}

	/* little hack, needed to signal: hey! we need a playlist, but don't
	 * free anything (there is nothing to be freed yet) */
	memset (&player, 0, sizeof (player));

	while (!doQuit) {
		/* song finished playing, clean up things/scrobble song */
		if (player.mode == PLAYER_FINISHED_PLAYBACK) {
			BarUiStartEventCmd (&settings, "songfinish", curStation, playlist,
					&player, PIANO_RET_OK);
			/* FIXME: pthread_join blocks everything if network connection
			 * is hung up e.g. */
			void *threadRet;
			pthread_join (playerThread, &threadRet);
			/* don't continue playback if thread reports error */
			if (threadRet != NULL) {
				curStation = NULL;
			}
			memset (&player, 0, sizeof (player));
		}

		/* check whether player finished playing and start playing new
		 * song */
		if (player.mode >= PLAYER_FINISHED_PLAYBACK ||
				player.mode == PLAYER_FREED) {
			if (curStation != NULL) {
				/* what's next? */
				if (playlist != NULL) {
					if (settings.history != 0) {
						/* prepend song to history list */
						PianoSong_t *tmpSong = songHistory;
						songHistory = playlist;
						/* select next song */
						playlist = playlist->next;
						songHistory->next = tmpSong;

						/* limit history's length */
						/* start with 1, so we're stopping at n-1 and have the
						 * chance to set ->next = NULL */
						unsigned int i = 1;
						tmpSong = songHistory;
						while (i < settings.history && tmpSong != NULL) {
							tmpSong = tmpSong->next;
							++i;
						}
						/* if too many songs in history... */
						if (tmpSong != NULL) {
							PianoSong_t *delSong = tmpSong->next;
							tmpSong->next = NULL;
							if (delSong != NULL) {
								PianoDestroyPlaylist (delSong);
							}
						}
					} else {
						/* don't keep history */
						playlist = playlist->next;
					}
				}
				if (playlist == NULL) {
					PianoReturn_t pRet = PIANO_RET_ERR;

					BarUiMsg (MSG_INFO, "Receiving new playlist... ");
					if ((pRet = BarUiPrintPianoStatus (PianoGetPlaylist (&ph,
							curStation->id, settings.audioFormat,
							&playlist))) != PIANO_RET_OK) {
						curStation = NULL;
					} else {
						if (playlist == NULL) {
							BarUiMsg (MSG_INFO, "No tracks left.\n");
							curStation = NULL;
						}
					}
					BarUiStartEventCmd (&settings, "stationfetchplaylist",
							curStation, playlist, &player, pRet);
				}
				/* song ready to play */
				if (playlist != NULL) {
					BarUiPrintSong (playlist, curStation->isQuickMix ?
							PianoFindStationById (ph.stations,
							playlist->stationId) : NULL);

					if (playlist->audioUrl == NULL) {
						BarUiMsg (MSG_ERR, "Invalid song url.\n");
					} else {
						/* setup player */
						memset (&player, 0, sizeof (player));

						WaitressInit (&player.waith);
						WaitressSetUrl (&player.waith, playlist->audioUrl);

						player.gain = playlist->fileGain;
						player.audioFormat = playlist->audioFormat;

						/* Setup dump directories. */
						prepare_dump_name(player.dump_filename, playlist);

						/* Setup dump handle. If we can read the file,
						 * then it already exists so don't re-write. */
						if (access(player.dump_filename, R_OK) != 0) {
							player.dump_handle = fopen(player.dump_filename, "w");
							BarUiMsg(MSG_INFO, "Will dump song...\n");
						} else {
							player.dump_handle = NULL;
							BarUiMsg(MSG_INFO, "Dump file found, will not dump!\n");
						}
			
						/* throw event */
						BarUiStartEventCmd (&settings, "songstart", curStation,
								playlist, &player, PIANO_RET_OK);

						/* start player */
						pthread_create (&playerThread, NULL, BarPlayerThread,
								&player);
					} /* end if audioUrl == NULL */
				} /* end if playlist != NULL */
			} /* end if curStation != NULL */
		}

		/* select modifies its arguments => copy the set */
		memcpy (&readSetCopy, &readSet, sizeof (readSet));
		selectTimeout.tv_sec = 1;
		selectTimeout.tv_usec = 0;

		/* in the meantime: wait for user actions */
		if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) {
			FILE *curFd = NULL;

			if (FD_ISSET(selectFds[0], &readSetCopy)) {
				curFd = stdin;
			} else if (FD_ISSET(selectFds[1], &readSetCopy)) {
				curFd = ctlFd;
			}
			buf = fgetc (curFd);

			size_t i;
			for (i = 0; i < BAR_KS_COUNT; i++) {
				if (settings.keys[i] == buf) {
					BarKeyShortcutFunc_t idToF[] = {BarUiActHelp,
							BarUiActLoveSong, BarUiActBanSong,
							BarUiActAddMusic, BarUiActCreateStation,
							BarUiActDeleteStation, BarUiActExplain,
							BarUiActStationFromGenre, BarUiActHistory,
							BarUiActSongInfo, BarUiActAddSharedStation,
							BarUiActMoveSong, BarUiActSkipSong, BarUiActPause,
							BarUiActQuit, BarUiActRenameStation,
							BarUiActSelectStation, BarUiActTempBanSong,
							BarUiActPrintUpcoming, BarUiActSelectQuickMix,
							BarUiActDebug, BarUiActBookmark};
					idToF[i] (&ph, &player, &settings, &playlist,
							&curStation, &songHistory, &doQuit, curFd);
					break;
				}
			}
		}

		/* show time */
		if (player.mode >= PLAYER_SAMPLESIZE_INITIALIZED &&
				player.mode < PLAYER_FINISHED_PLAYBACK) {
			/* Ugly: songDuration is unsigned _long_ int! Lets hope this won't
			 * overflow */
			int songRemaining = (signed long int) (player.songDuration - player.songPlayed)
					/ BAR_PLAYER_MS_TO_S_FACTOR;
			char pos = 0;
			if (songRemaining < 0) {
				/* Use plus sign if song is longer than expected */
				pos = 1;
				songRemaining = -songRemaining;
			}
			BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'),
					songRemaining / 60, songRemaining % 60,
					player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60,
					player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60);
		}
	}

	/* destroy everything (including the world...) */
	if (player.mode != PLAYER_FREED) {
		pthread_join (playerThread, NULL);
	}
	if (ctlFd != NULL) {
		fclose (ctlFd);
	}
	PianoDestroy (&ph);
	PianoDestroyPlaylist (songHistory);
	PianoDestroyPlaylist (playlist);
	ao_shutdown();
	BarSettingsDestroy (&settings);

	/* restore terminal attributes, zsh doesn't need this, bash does... */
	BarTermRestore (&termOrig);

	return 0;
}
Esempio n. 3
0
int main (int argc, char **argv) {
	static BarApp_t app;
	pthread_t playerThread;
	/* FIXME: max path length? */
	char ctlPath[1024];
	FILE *ctlFd = NULL;
	struct timeval selectTimeout;
	int maxFd, selectFds[2];
	fd_set readSet, readSetCopy;
	char buf = '\0';
	/* terminal attributes _before_ we started messing around with ~ECHO */
	struct termios termOrig;

	memset (&app, 0, sizeof (app));

	/* save terminal attributes, before disabling echoing */
	BarTermSave (&termOrig);

	BarTermSetEcho (0);
	BarTermSetBuffer (0);
	/* init some things */
	ao_initialize ();
	PianoInit (&app.ph);

	WaitressInit (&app.waith);
	strncpy (app.waith.host, PIANO_RPC_HOST, sizeof (app.waith.host)-1);
	strncpy (app.waith.port, PIANO_RPC_PORT, sizeof (app.waith.port)-1);

	BarSettingsInit (&app.settings);
	BarSettingsRead (&app.settings);

	BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "! Press %c for a list of commands.\n",
			app.settings.keys[BAR_KS_HELP]);

	/* init fds */
	FD_ZERO(&readSet);
	selectFds[0] = fileno (stdin);
	FD_SET(selectFds[0], &readSet);
	maxFd = selectFds[0] + 1;

	BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath));
	/* FIXME: why is r_+_ required? */
	ctlFd = fopen (ctlPath, "r+");
	if (ctlFd != NULL) {
		selectFds[1] = fileno (ctlFd);
		FD_SET(selectFds[1], &readSet);
		/* assuming ctlFd is always > stdin */
		maxFd = selectFds[1] + 1;
		BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath);
	}

	if (app.settings.username == NULL) {
		char nameBuf[100];
		BarUiMsg (MSG_QUESTION, "Username: "******"Password: "******"Login... ");
		if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_LOGIN, &app.waith,
				&reqData, &pRet, &wRet)) {
			BarTermRestore (&termOrig);
			return 0;
		}
	}

	{
		PianoReturn_t pRet;
		WaitressReturn_t wRet;

		BarUiMsg (MSG_INFO, "Get stations... ");
		if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_STATIONS, &app.waith,
				NULL, &pRet, &wRet)) {
			BarTermRestore (&termOrig);
			return 0;
		}
	}

	/* try to get autostart station */
	if (app.settings.autostartStation != NULL) {
		app.curStation = PianoFindStationById (app.ph.stations,
				app.settings.autostartStation);
		if (app.curStation == NULL) {
			BarUiMsg (MSG_ERR, "Error: Autostart station not found.\n");
		}
	}
	/* no autostart? ask the user */
	if (app.curStation == NULL) {
		app.curStation = BarUiSelectStation (&app.ph, "Select station: ",
				app.settings.sortOrder, stdin);
	}
	if (app.curStation != NULL) {
		BarUiPrintStation (app.curStation);
	}

	/* little hack, needed to signal: hey! we need a playlist, but don't
	 * free anything (there is nothing to be freed yet) */
	memset (&app.player, 0, sizeof (app.player));

	while (!app.doQuit) {
		/* song finished playing, clean up things/scrobble song */
		if (app.player.mode == PLAYER_FINISHED_PLAYBACK) {
			BarUiStartEventCmd (&app.settings, "songfinish", app.curStation,
					app.playlist, &app.player, PIANO_RET_OK, WAITRESS_RET_OK);
			/* FIXME: pthread_join blocks everything if network connection
			 * is hung up e.g. */
			void *threadRet;
			pthread_join (playerThread, &threadRet);
			/* don't continue playback if thread reports error */
			if (threadRet != (void *) PLAYER_RET_OK) {
				app.curStation = NULL;
			}
			memset (&app.player, 0, sizeof (app.player));
		}

		/* check whether player finished playing and start playing new
		 * song */
		if (app.player.mode >= PLAYER_FINISHED_PLAYBACK ||
				app.player.mode == PLAYER_FREED) {
			if (app.curStation != NULL) {
				/* what's next? */
				if (app.playlist != NULL) {
					if (app.settings.history != 0) {
						/* prepend song to history list */
						PianoSong_t *tmpSong = app.songHistory;
						app.songHistory = app.playlist;
						/* select next song */
						app.playlist = app.playlist->next;
						app.songHistory->next = tmpSong;

						/* limit history's length */
						/* start with 1, so we're stopping at n-1 and have the
						 * chance to set ->next = NULL */
						unsigned int i = 1;
						tmpSong = app.songHistory;
						while (i < app.settings.history && tmpSong != NULL) {
							tmpSong = tmpSong->next;
							++i;
						}
						/* if too many songs in history... */
						if (tmpSong != NULL) {
							PianoSong_t *delSong = tmpSong->next;
							tmpSong->next = NULL;
							if (delSong != NULL) {
								PianoDestroyPlaylist (delSong);
							}
						}
					} else {
						/* don't keep history */
						app.playlist = app.playlist->next;
					}
				}
				if (app.playlist == NULL) {
					PianoReturn_t pRet;
					WaitressReturn_t wRet;
					PianoRequestDataGetPlaylist_t reqData;
					reqData.station = app.curStation;
					reqData.format = app.settings.audioFormat;

					BarUiMsg (MSG_INFO, "Receiving new playlist... ");
					if (!BarUiPianoCall (&app.ph, PIANO_REQUEST_GET_PLAYLIST,
							&app.waith, &reqData, &pRet, &wRet)) {
						app.curStation = NULL;
					} else {
						app.playlist = reqData.retPlaylist;
						if (app.playlist == NULL) {
							BarUiMsg (MSG_INFO, "No tracks left.\n");
							app.curStation = NULL;
						}
					}
					BarUiStartEventCmd (&app.settings, "stationfetchplaylist",
							app.curStation, app.playlist, &app.player, pRet,
							wRet);
				}
				/* song ready to play */
				if (app.playlist != NULL) {
					BarUiPrintSong (app.playlist, app.curStation->isQuickMix ?
							PianoFindStationById (app.ph.stations,
							app.playlist->stationId) : NULL);

					if (app.playlist->audioUrl == NULL) {
						BarUiMsg (MSG_ERR, "Invalid song url.\n");
					} else {
						/* setup player */
						memset (&app.player, 0, sizeof (app.player));

						WaitressInit (&app.player.waith);
						WaitressSetUrl (&app.player.waith, app.playlist->audioUrl);

						/* set up global proxy, player is NULLed on songfinish */
						if (app.settings.proxy != NULL) {
							char tmpPath[2];
							WaitressSplitUrl (app.settings.proxy,
									app.player.waith.proxyHost,
									sizeof (app.player.waith.proxyHost),
									app.player.waith.proxyPort,
									sizeof (app.player.waith.proxyPort), tmpPath,
									sizeof (tmpPath));
						}

						app.player.gain = app.playlist->fileGain;
						app.player.audioFormat = app.playlist->audioFormat;
			
						/* throw event */
						BarUiStartEventCmd (&app.settings, "songstart",
								app.curStation, app.playlist, &app.player,
								PIANO_RET_OK, WAITRESS_RET_OK);

						/* prevent race condition, mode must _not_ be FREED if
						 * thread has been started */
						app.player.mode = PLAYER_STARTING;
						/* start player */
						pthread_create (&playerThread, NULL, BarPlayerThread,
								&app.player);
					} /* end if audioUrl == NULL */
				} /* end if playlist != NULL */
			} /* end if curStation != NULL */
		}

		/* select modifies its arguments => copy the set */
		memcpy (&readSetCopy, &readSet, sizeof (readSet));
		selectTimeout.tv_sec = 1;
		selectTimeout.tv_usec = 0;

		/* in the meantime: wait for user actions */
		if (select (maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) {
			FILE *curFd = NULL;

			if (FD_ISSET(selectFds[0], &readSetCopy)) {
				curFd = stdin;
			} else if (FD_ISSET(selectFds[1], &readSetCopy)) {
				curFd = ctlFd;
			}
			buf = fgetc (curFd);

			size_t i;
			for (i = 0; i < BAR_KS_COUNT; i++) {
				if (app.settings.keys[i] == buf) {
					static const BarKeyShortcutFunc_t idToF[] = {BarUiActHelp,
							BarUiActLoveSong, BarUiActBanSong,
							BarUiActAddMusic, BarUiActCreateStation,
							BarUiActDeleteStation, BarUiActExplain,
							BarUiActStationFromGenre, BarUiActHistory,
							BarUiActSongInfo, BarUiActAddSharedStation,
							BarUiActMoveSong, BarUiActSkipSong, BarUiActPause,
							BarUiActQuit, BarUiActRenameStation,
							BarUiActSelectStation, BarUiActTempBanSong,
							BarUiActPrintUpcoming, BarUiActSelectQuickMix,
							BarUiActDebug, BarUiActBookmark};
					idToF[i] (&app, curFd);
					break;
				}
			}
		}

		/* show time */
		if (app.player.mode >= PLAYER_SAMPLESIZE_INITIALIZED &&
				app.player.mode < PLAYER_FINISHED_PLAYBACK) {
			/* Ugly: songDuration is unsigned _long_ int! Lets hope this won't
			 * overflow */
			int songRemaining = (signed long int) (app.player.songDuration -
					app.player.songPlayed) / BAR_PLAYER_MS_TO_S_FACTOR;
			char pos = 0;
			if (songRemaining < 0) {
				/* Use plus sign if song is longer than expected */
				pos = 1;
				songRemaining = -songRemaining;
			}
			BarUiMsg (MSG_TIME, "%c%02i:%02i/%02i:%02i\r", (pos ? '+' : '-'),
					songRemaining / 60, songRemaining % 60,
					app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60,
					app.player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60);
		}
	}

	/* destroy everything (including the world...) */
	if (app.player.mode != PLAYER_FREED) {
		pthread_join (playerThread, NULL);
	}
	if (ctlFd != NULL) {
		fclose (ctlFd);
	}
	PianoDestroy (&app.ph);
	PianoDestroyPlaylist (app.songHistory);
	PianoDestroyPlaylist (app.playlist);
	ao_shutdown();
	BarSettingsDestroy (&app.settings);

	/* restore terminal attributes, zsh doesn't need this, bash does... */
	BarTermRestore (&termOrig);

	return 0;
}
Esempio n. 4
0
/*	Parse url and set host, port, path
 *	@param Waitress handle
 *	@param url: protocol://host:port/path
 */
inline char WaitressSetUrl (WaitressHandle_t *waith, const char *url) {
	return WaitressSplitUrl (url, waith->host, sizeof (waith->host),
		waith->port, sizeof (waith->port), waith->path, sizeof (waith->path));
}