/* piano wrapper: prepare/execute http request and pass result back to * libpiano (updates data structures) * @param piano handle * @param request type * @param waitress handle * @param data pointer (used as request data) * @return 1 on success, 0 otherwise */ int BarUiPianoCall (PianoHandle_t *ph, PianoRequestType_t type, WaitressHandle_t *waith, void *data, PianoReturn_t *pRet, WaitressReturn_t *wRet) { PianoRequest_t req; memset (&req, 0, sizeof (req)); /* repeat as long as there are http requests to do */ do { req.data = data; *pRet = PianoRequest (ph, &req, type); if (*pRet != PIANO_RET_OK) { BarUiMsg (MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); return 0; } *wRet = BarPianoHttpRequest (waith, &req); if (*wRet != WAITRESS_RET_OK) { BarUiMsg (MSG_NONE, "Network error: %s\n", WaitressErrorToStr (*wRet)); PianoDestroyRequest (&req); if (req.responseData != NULL) { free (req.responseData); } return 0; } *pRet = PianoResponse (ph, &req); if (*pRet != PIANO_RET_CONTINUE_REQUEST) { if (*pRet != PIANO_RET_OK) { BarUiMsg (MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); if (req.responseData != NULL) { free (req.responseData); } return 0; } else { BarUiMsg (MSG_NONE, "Ok.\n"); } } /* we can destroy the request at this point, even when this call needs * more than one http request. persistend data (step counter, e.g.) is * stored in req.data */ if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); } while (*pRet == PIANO_RET_CONTINUE_REQUEST); return 1; }
/* prints human readable status message based on return value * @param piano return value */ inline PianoReturn_t BarUiPrintPianoStatus (PianoReturn_t ret) { if (ret != PIANO_RET_OK) { BarUiMsg (MSG_NONE, "Error: %s\n", PianoErrorToStr (ret)); } else { BarUiMsg (MSG_NONE, "Ok.\n"); } return ret; }
/* Excute external event handler * @param settings containing the cmdline * @param event type * @param current station * @param current song * @param piano error-code (PIANO_RET_OK if not applicable) * @param waitress error-code (WAITRESS_RET_OK if not applicable) */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, const struct audioPlayer *player, PianoStation_t *stations, PianoReturn_t pRet, WaitressReturn_t wRet) { pid_t chld; int pipeFd[2]; if (settings->eventCmd == NULL) { /* nothing to do... */ return; } if (pipe (pipeFd) == -1) { BarUiMsg (settings, MSG_ERR, "Cannot create eventcmd pipe. (%s)\n", strerror (errno)); return; } chld = fork (); if (chld == 0) { /* child */ close (pipeFd[1]); dup2 (pipeFd[0], fileno (stdin)); execl (settings->eventCmd, settings->eventCmd, type, (char *) NULL); BarUiMsg (settings, MSG_ERR, "Cannot start eventcmd. (%s)\n", strerror (errno)); close (pipeFd[0]); exit (1); } else if (chld == -1) { BarUiMsg (settings, MSG_ERR, "Cannot fork eventcmd. (%s)\n", strerror (errno)); } else { /* parent */ int status; PianoStation_t *songStation = NULL; FILE *pipeWriteFd; close (pipeFd[0]); pipeWriteFd = fdopen (pipeFd[1], "w"); if (curSong != NULL && stations != NULL && curStation->isQuickMix) { songStation = PianoFindStationById (stations, curSong->stationId); } fprintf (pipeWriteFd, "artist=%s\n" "title=%s\n" "album=%s\n" "coverArt=%s\n" "stationName=%s\n" "songStationName=%s\n" "pRet=%i\n" "pRetStr=%s\n" "wRet=%i\n" "wRetStr=%s\n" "songDuration=%lu\n" "songPlayed=%lu\n" "rating=%i\n" "detailUrl=%s\n", curSong == NULL ? "" : curSong->artist, curSong == NULL ? "" : curSong->title, curSong == NULL ? "" : curSong->album, curSong == NULL ? "" : curSong->coverArt, curStation == NULL ? "" : curStation->name, songStation == NULL ? "" : songStation->name, pRet, PianoErrorToStr (pRet), wRet, WaitressErrorToStr (wRet), player->songDuration, player->songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating, curSong == NULL ? "" : curSong->detailUrl ); if (stations != NULL) { /* send station list */ PianoStation_t **sortedStations = NULL; size_t stationCount; sortedStations = BarSortedStations (stations, &stationCount, settings->sortOrder); assert (sortedStations != NULL); fprintf (pipeWriteFd, "stationCount=%zd\n", stationCount); for (size_t i = 0; i < stationCount; i++) { const PianoStation_t *currStation = sortedStations[i]; fprintf (pipeWriteFd, "station%zd=%s\n", i, currStation->name); } free (sortedStations); } else { const char * const msg = "stationCount=0\n"; fwrite (msg, sizeof (*msg), strlen (msg), pipeWriteFd); } /* closes pipeFd[1] as well */ fclose (pipeWriteFd); /* wait to get rid of the zombie */ waitpid (chld, &status, 0); } }
/* piano wrapper: prepare/execute http request and pass result back to * libpiano (updates data structures) * @param app handle * @param request type * @param request data * @param stores piano return code * @param stores waitress return code * @return 1 on success, 0 otherwise */ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type, void *data, PianoReturn_t *pRet, WaitressReturn_t *wRet) { PianoRequest_t req; memset (&req, 0, sizeof (req)); /* repeat as long as there are http requests to do */ do { req.data = data; *pRet = PianoRequest (&app->ph, &req, type); if (*pRet != PIANO_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); return 0; } *wRet = BarPianoHttpRequest (&app->waith, &req); if (*wRet != WAITRESS_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Network error: %s\n", WaitressErrorToStr (*wRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } *pRet = PianoResponse (&app->ph, &req); if (*pRet != PIANO_RET_CONTINUE_REQUEST) { /* checking for request type avoids infinite loops */ if (*pRet == PIANO_RET_P_INVALID_AUTH_TOKEN && type != PIANO_REQUEST_LOGIN) { /* reauthenticate */ PianoReturn_t authpRet; WaitressReturn_t authwRet; PianoRequestDataLogin_t reqData; reqData.user = app->settings.username; reqData.password = app->settings.password; reqData.step = 0; BarUiMsg (&app->settings, MSG_NONE, "Reauthentication required... "); if (!BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &authpRet, &authwRet)) { *pRet = authpRet; *wRet = authwRet; if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { /* try again */ *pRet = PIANO_RET_CONTINUE_REQUEST; BarUiMsg (&app->settings, MSG_INFO, "Trying again... "); } } else if (*pRet != PIANO_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { BarUiMsg (&app->settings, MSG_NONE, "Ok.\n"); } } /* we can destroy the request at this point, even when this call needs * more than one http request. persistent data (step counter, e.g.) is * stored in req.data */ if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); } while (*pRet == PIANO_RET_CONTINUE_REQUEST); return 1; }
int main (int argc, char **argv) { static BarApp_t app; memset (&app, 0, sizeof (app)); /* save terminal attributes, before disabling echoing */ BarTermInit (); /* signals */ signal (SIGPIPE, SIG_IGN); /* init some things */ gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); BarPlayerInit (); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); PianoReturn_t pret; if ((pret = PianoInit (&app.ph, app.settings.partnerUser, app.settings.partnerPassword, app.settings.device, app.settings.inkey, app.settings.outkey)) != PIANO_RET_OK) { BarUiMsg (&app.settings, MSG_ERR, "Initialization failed:" " %s\n", PianoErrorToStr (pret)); return 0; } BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } curl_global_init (CURL_GLOBAL_DEFAULT); app.http = curl_easy_init (); assert (app.http != NULL); /* init fds */ FD_ZERO(&app.input.set); int fifo_uses_this_fd = 0; app.input.fds[0] = STDIN_FILENO; if (isatty(fileno(stdin))) { fifo_uses_this_fd = 1; FD_SET(app.input.fds[0], &app.input.set); } /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[fifo_uses_this_fd] = open (app.settings.fifo, O_RDWR); if (app.input.fds[fifo_uses_this_fd] != -1) { struct stat s; /* check for file type, must be fifo */ fstat (app.input.fds[fifo_uses_this_fd], &s); if (!S_ISFIFO (s.st_mode)) { BarUiMsg (&app.settings, MSG_ERR, "File at %s is not a fifo\n", app.settings.fifo); close (app.input.fds[fifo_uses_this_fd]); app.input.fds[fifo_uses_this_fd] = -1; } else { FD_SET(app.input.fds[fifo_uses_this_fd], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", app.settings.fifo); } } app.input.maxfd = app.input.fds[0]; if (fifo_uses_this_fd>0) app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[fifo_uses_this_fd] != -1) { close (app.input.fds[fifo_uses_this_fd]); } /* write statefile */ BarSettingsWrite (app.curStation, &app.settings); PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); curl_easy_cleanup (app.http); curl_global_cleanup (); BarPlayerDestroy (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (); return 0; }
int MythPianoService::PianoCall(PianoRequestType_t type, void *data, PianoReturn_t *pRet, WaitressReturn_t *wRet) { if (!m_Piano) return -1; PianoRequest_t req; memset (&req, 0, sizeof (req)); /* repeat as long as there are http requests to do */ do { req.data = data; *pRet = PianoRequest (m_Piano, &req, type); if (*pRet != PIANO_RET_OK) { BroadcastMessage("Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); return 0; } *wRet = PianoHttpRequest(&m_Waith, &req); if (*wRet != WAITRESS_RET_OK) { BroadcastMessage ("Network error: %s\n", WaitressErrorToStr (*wRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } *pRet = PianoResponse (m_Piano, &req); if (*pRet != PIANO_RET_CONTINUE_REQUEST) { /* checking for request type avoids infinite loops */ if (*pRet == PIANO_RET_AUTH_TOKEN_INVALID && type != PIANO_REQUEST_LOGIN) { /* reauthenticate */ PianoReturn_t authpRet; WaitressReturn_t authwRet; PianoRequestDataLogin_t reqData; QString username = gCoreContext->GetSetting("pandora-username"); QString password = gCoreContext->GetSetting("pandora-password"); //wtf really? char* usernameBuff = strndup(username.toUtf8().data(), 1024); char* passwordBuff = strndup(password.toUtf8().data(), 1024); reqData.user = usernameBuff; reqData.password = passwordBuff; reqData.step = 0; BroadcastMessage ("Reauthentication required... "); if (!PianoCall(PIANO_REQUEST_LOGIN, &reqData, &authpRet, &authwRet)) { *pRet = authpRet; *wRet = authwRet; if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { /* try again */ *pRet = PIANO_RET_CONTINUE_REQUEST; BroadcastMessage("Trying again... "); } // wtf free(usernameBuff); free(passwordBuff); } else if (*pRet != PIANO_RET_OK) { BroadcastMessage("Error: %s\n", PianoErrorToStr (*pRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { BroadcastMessage("Login Ok.\n"); } } /* we can destroy the request at this point, even when this call needs * more than one http request. persistent data (step counter, e.g.) is * stored in req.data */ if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); } while (*pRet == PIANO_RET_CONTINUE_REQUEST); return 1; }
int main (int argc, char **argv) { static BarApp_t app; /* 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); /* signals */ signal (SIGPIPE, SIG_IGN); /* init some things */ gcry_check_version (NULL); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); gnutls_global_init (); BarPlayerInit (); BarSettingsInit (&app.settings); BarSettingsRead (&app.settings); PianoReturn_t pret; if ((pret = PianoInit (&app.ph, app.settings.partnerUser, app.settings.partnerPassword, app.settings.device, app.settings.inkey, app.settings.outkey)) != PIANO_RET_OK) { BarUiMsg (&app.settings, MSG_ERR, "Initialization failed:" " %s\n", PianoErrorToStr (pret)); return 0; } BarUiMsg (&app.settings, MSG_NONE, "Welcome to " PACKAGE " (" VERSION ")! "); if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) { BarUiMsg (&app.settings, MSG_NONE, "\n"); } else { BarUiMsg (&app.settings, MSG_NONE, "Press %c for a list of commands.\n", app.settings.keys[BAR_KS_HELP]); } WaitressInit (&app.waith); app.waith.url.host = app.settings.rpcHost; app.waith.url.tlsPort = app.settings.rpcTlsPort; app.waith.tlsFingerprint = app.settings.tlsFingerprint; /* init fds */ FD_ZERO(&app.input.set); app.input.fds[0] = STDIN_FILENO; FD_SET(app.input.fds[0], &app.input.set); /* open fifo read/write so it won't EOF if nobody writes to it */ assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2); app.input.fds[1] = open (app.settings.fifo, O_RDWR); if (app.input.fds[1] != -1) { struct stat s; /* check for file type, must be fifo */ fstat (app.input.fds[1], &s); if (!S_ISFIFO (s.st_mode)) { BarUiMsg (&app.settings, MSG_ERR, "File at %s is not a fifo\n", app.settings.fifo); close (app.input.fds[1]); app.input.fds[1] = -1; } else { FD_SET(app.input.fds[1], &app.input.set); BarUiMsg (&app.settings, MSG_INFO, "Control fifo at %s opened\n", app.settings.fifo); } } app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] : app.input.fds[1]; ++app.input.maxfd; BarMainLoop (&app); if (app.input.fds[1] != -1) { close (app.input.fds[1]); } /* write statefile */ BarSettingsWrite (app.curStation, &app.settings); PianoDestroy (&app.ph); PianoDestroyPlaylist (app.songHistory); PianoDestroyPlaylist (app.playlist); WaitressFree (&app.waith); BarPlayerDestroy (); gnutls_global_deinit (); BarSettingsDestroy (&app.settings); /* restore terminal attributes, zsh doesn't need this, bash does... */ BarTermRestore (&termOrig); return 0; }
/* Excute external event handler * @param settings containing the cmdline * @param event type * @param current station * @param current song * @param piano error-code (PIANO_RET_OK if not applicable) * @param waitress error-code (WAITRESS_RET_OK if not applicable) */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, const struct audioPlayer *player, PianoStation_t *stations, PianoReturn_t pRet, WaitressReturn_t wRet) { int status; PianoStation_t *songStation = NULL; if (curSong != NULL && stations != NULL && curStation->isQuickMix) { songStation = PianoFindStationById (stations, curSong->stationId); } printf ("current\tartist=%s\n" "current\ttitle=%s\n" "current\talbum=%s\n" "current\tcoverArt=%s\n" "current\tstationName=%s\n" "songStationName=%s\n" "pRet=%i\n" "pRetStr=%s\n" "wRet=%i\n" "wRetStr=%s\n" "songDuration=%lu\n" "songPlayed=%lu\n" "rating=%i\n" "detailUrl=%s\n", curSong == NULL ? "" : curSong->artist, curSong == NULL ? "" : curSong->title, curSong == NULL ? "" : curSong->album, curSong == NULL ? "" : curSong->coverArt, curStation == NULL ? "" : curStation->name, songStation == NULL ? "" : songStation->name, pRet, PianoErrorToStr (pRet), wRet, WaitressErrorToStr (wRet), player->songDuration, player->songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating, curSong == NULL ? "" : curSong->detailUrl ); if (stations != NULL) { /* send station list */ PianoStation_t **sortedStations = NULL; size_t stationCount; sortedStations = BarSortedStations (stations, &stationCount, settings->sortOrder); assert (sortedStations != NULL); printf ("station\tstationCount=%zd\n", stationCount); for (size_t i = 0; i < stationCount; i++) { const PianoStation_t *currStation = sortedStations[i]; printf ("station\tstation%zd=%s\n", i, currStation->name); } free (sortedStations); } }
/* piano wrapper: prepare/execute http request and pass result back to * libpiano */ bool BarUiPianoCall (BarApp_t * const app, const PianoRequestType_t type, void * const data, PianoReturn_t * const pRet, CURLcode * const wRet) { PianoReturn_t pRetLocal = PIANO_RET_OK; CURLcode wRetLocal = CURLE_OK; bool ret = false; /* repeat as long as there are http requests to do */ do { PianoRequest_t req = { .data = data, .responseData = NULL }; pRetLocal = PianoRequest (&app->ph, &req, type); if (pRetLocal != PIANO_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (pRetLocal)); goto cleanup; } wRetLocal = BarPianoHttpRequest (app->http, &app->settings, &req); if (wRetLocal == CURLE_ABORTED_BY_CALLBACK) { BarUiMsg (&app->settings, MSG_NONE, "Interrupted.\n"); goto cleanup; } else if (wRetLocal != CURLE_OK) { BarUiMsg (&app->settings, MSG_NONE, "Network error: %s\n", curl_easy_strerror (wRetLocal)); goto cleanup; } pRetLocal = PianoResponse (&app->ph, &req); if (pRetLocal != PIANO_RET_CONTINUE_REQUEST) { /* checking for request type avoids infinite loops */ if (pRetLocal == PIANO_RET_P_INVALID_AUTH_TOKEN && type != PIANO_REQUEST_LOGIN) { /* reauthenticate */ PianoRequestDataLogin_t reqData; reqData.user = app->settings.username; reqData.password = app->settings.password; reqData.step = 0; BarUiMsg (&app->settings, MSG_NONE, "Reauthentication required... "); if (!BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &pRetLocal, &wRetLocal)) { goto cleanup; } else { /* try again */ pRetLocal = PIANO_RET_CONTINUE_REQUEST; BarUiMsg (&app->settings, MSG_INFO, "Trying again... "); } } else if (pRetLocal != PIANO_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (pRetLocal)); goto cleanup; } else { BarUiMsg (&app->settings, MSG_NONE, "Ok.\n"); ret = true; } } cleanup: /* persistent data is stored in req.data */ free (req.responseData); PianoDestroyRequest (&req); } while (pRetLocal == PIANO_RET_CONTINUE_REQUEST); *pRet = pRetLocal; *wRet = wRetLocal; return ret; }
/* Excute external event handler * @param settings containing the cmdline * @param event type * @param current station * @param current song * @param piano error-code (PIANO_RET_OK if not applicable) * @param waitress error-code (WAITRESS_RET_OK if not applicable) */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, const struct audioPlayer *player, PianoReturn_t pRet, WaitressReturn_t wRet) { pid_t chld; char pipeBuf[1024]; int pipeFd[2]; if (settings->eventCmd == NULL) { /* nothing to do... */ return; } /* prepare stdin content */ memset (pipeBuf, 0, sizeof (pipeBuf)); snprintf (pipeBuf, sizeof (pipeBuf), "artist=%s\n" "title=%s\n" "album=%s\n" "stationName=%s\n" "pRet=%i\n" "pRetStr=%s\n" "wRet=%i\n" "wRetStr=%s\n" "songDuration=%lu\n" "songPlayed=%lu\n" "rating=%i\n", curSong == NULL ? "" : curSong->artist, curSong == NULL ? "" : curSong->title, curSong == NULL ? "" : curSong->album, curStation == NULL ? "" : curStation->name, pRet, PianoErrorToStr (pRet), wRet, WaitressErrorToStr (wRet), player->songDuration, player->songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating ); if (pipe (pipeFd) == -1) { BarUiMsg (MSG_ERR, "Cannot create eventcmd pipe. (%s)\n", strerror (errno)); return; } chld = fork (); if (chld == 0) { /* child */ close (pipeFd[1]); dup2 (pipeFd[0], fileno (stdin)); execl (settings->eventCmd, settings->eventCmd, type, (char *) NULL); BarUiMsg (MSG_ERR, "Cannot start eventcmd. (%s)\n", strerror (errno)); close (pipeFd[0]); exit (1); } else if (chld == -1) { BarUiMsg (MSG_ERR, "Cannot fork eventcmd. (%s)\n", strerror (errno)); } else { /* parent */ int status; close (pipeFd[0]); write (pipeFd[1], pipeBuf, strlen (pipeBuf)); close (pipeFd[1]); /* wait to get rid of the zombie */ waitpid (chld, &status, 0); } }