users->isLoggedIn()) { $uid = $page->userdata['id']; $apiKey = $page->userdata['rsstoken']; $catExclusions = $page->userdata['categoryexclusions']; $maxRequests = $page->userdata['apirequests']; } else { if ($function != 'c' && $function != 'r') { if (!isset($_GET['apikey'])) { showApiError(200, 'Missing parameter (apikey)'); } else { $res = $page->users->getByRssToken($_GET['apikey']); $apiKey = $_GET['apikey']; if (!$res) { showApiError(100, 'Incorrect user credentials (wrong API key)'); } $uid = $res['id']; $catExclusions = $page->users->getCategoryExclusion($uid); $maxRequests = $res['apirequests']; } } } $page->smarty->assign('uid', $uid); $page->smarty->assign('rsstoken', $apiKey); // Record user access to the api, if its been called by a user (i.e. capabilities request do not require a user to be logged in or key provided). if ($uid != '') { $page->users->updateApiAccessed($uid); $apiRequests = $page->users->getApiRequests($uid); if ($apiRequests > $maxRequests) { showApiError(500, 'Request limit reached (' . $apiRequests . '/' . $maxRequests . ')'); } } $releases = new Releases(['Settings' => $page->settings]); $page->smarty->assign('extended', (isset($_GET['extended']) && $_GET['extended'] == 1 ? '1' : '0')); $page->smarty->assign('del', (isset($_GET['del']) && $_GET['del'] == 1 ? '1' : '0')); // Output is either json or xml. $outputXML = (isset($_GET['o']) && $_GET['o'] == 'json' ? false : true); switch ($function) { // Search releases. case 's': verifyEmptyParameter('q'); $maxAge = maxAge(); $page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']); $categoryID = categoryID(); $limit = limit(); $offset = offset(); if (isset($_GET['q'])) { $relData = $releases->search( $_GET['q'], -1, -1, -1, -1, -1, -1, 0, 0, -1, -1, $offset, $limit, '', $maxAge, $catExclusions, "basic", $categoryID ); } else { $totalRows = $releases->getBrowseCount($categoryID, $maxAge, $catExclusions); $relData = $releases->getBrowseRange($categoryID, $offset, $limit, '', $maxAge, $catExclusions); } printOutput($relData, $outputXML, $page, $offset); break; // Search tv releases. case 'tv': verifyEmptyParameter('q'); verifyEmptyParameter('vid'); verifyEmptyParameter('tvdbid'); verifyEmptyParameter('traktid'); verifyEmptyParameter('rid'); verifyEmptyParameter('tvmazeid'); verifyEmptyParameter('imdbid'); verifyEmptyParameter('tmdbid'); verifyEmptyParameter('season'); verifyEmptyParameter('ep'); $maxAge = maxAge(); $page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']); $offset = offset(); $siteIdArr = [ 'id' => (isset($_GET['vid']) ? $_GET['vid'] : '0'), 'tvdb' => (isset($_GET['tvdbid']) ? $_GET['tvdbid'] : '0'), 'trakt' => (isset($_GET['traktid']) ? $_GET['traktid'] : '0'), 'tvrage' => (isset($_GET['rid']) ? $_GET['rid'] : '0'), 'tvmaze' => (isset($_GET['tvmazeid']) ? $_GET['tvmazeid'] : '0'), 'imdb' => (isset($_GET['imdbid']) ? $_GET['imdbid'] : '0'), 'tmdb' => (isset($_GET['tmdbid']) ? $_GET['tmdbid'] : '0') ]; if (isset($_GET['season']) && isset($_GET['ep'])) { if (preg_match('#\d{4}#i', $_GET['season'], $year) && stripos($_GET['ep'], '/') !== false) { $airdate = $year[0] . '/' . $_GET['ep']; } else { $series = $_GET['season']; $episode = $_GET['ep']; } } $relData = $releases->searchShows( $siteIdArr, (isset($series) ? $series : ''), (isset($episode) ? $episode : ''), (isset($airdate) ? $airdate : ''), $offset, limit(), (isset($_GET['q']) ? $_GET['q'] : ''), categoryID(), $maxAge ); addLanguage($relData, $page->settings); printOutput($relData, $outputXML, $page, $offset); break; // Search movie releases. case 'm': verifyEmptyParameter('q'); verifyEmptyParameter('imdbid'); $maxAge = maxAge(); $page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']); $offset = offset(); $imdbId = (isset($_GET['imdbid']) ? $_GET['imdbid'] : '-1'); $relData = $releases->searchbyImdbId( $imdbId, $offset, limit(), (isset($_GET['q']) ? $_GET['q'] : ''), categoryID(), $maxAge ); addCoverURL($relData, function($release) { return Utility::getCoverURL(['type' => 'movies', 'id' => $release['imdbid']]); } ); addLanguage($relData, $page->settings); printOutput($relData, $outputXML, $page, $offset); break; // Get NZB. case 'g': if (!isset($_GET['id'])) { showApiError(200, 'Missing parameter (id is required for downloading an NZB)'); } $relData = $releases->getByGuid($_GET['id']); if ($relData) { header( 'Location:' . WWW_TOP . '/getnzb?i=' . $uid . '&r=' . $apiKey . '&id=' . $relData['guid'] . ((isset($_GET['del']) && $_GET['del'] == '1') ? '&del=1' : '') ); } else { showApiError(300, 'No such item (the guid you provided has no release in our database)'); } break; // Get individual NZB details. case 'd': if (!isset($_GET['id'])) { showApiError(200, 'Missing parameter (id is required for downloading an NZB)'); } $page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']); $data = $releases->getByGuid($_GET['id']); $relData = []; if ($data) { $relData[] = $data; } printOutput($relData, $outputXML, $page, offset()); break; // Get an NFO file for an individual release. case 'n': if (!isset($_GET['id'])) { showApiError(200, 'Missing parameter (id is required for retrieving an NFO)'); } $page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']); $rel = $releases->getByGuid($_GET["id"]); $data = $releases->getReleaseNfo($rel['id']); if ($rel !== false && !empty($rel)) { if ($data !== false) { if (isset($_GET['o']) && $_GET['o'] == 'file') { header("Content-type: application/octet-stream"); header("Content-disposition: attachment; filename={$rel['searchname']}.nfo"); exit($data['nfo']); } else { echo nl2br(Utility::cp437toUTF($data['nfo'])); } } else { showApiError(300, 'Release does not have an NFO file associated.'); } } else { showApiError(300, 'Release does not exist.'); } break; // Capabilities request. case 'c': //get categories $category = new Category(['Settings' => $page->settings]); $cats = $category->getForMenu(); //insert cats into template variable $page->smarty->assign('parentcatlist', $cats); if ($outputXML) { //use apicaps.tpl if xml is requested $response = $page->smarty->fetch('apicaps.tpl'); header('Content-type: text/xml'); header('Content-Length: ' . strlen($response) ); echo $response; } else { //otherwise construct array of capabilities and categories //get capabilities $caps = (new Capabilities(['Settings' => $page->settings]))->getForMenu(); $caps['categories'] = $category->getForMenu(); //use json_encode $response = encodeAsJSON($caps); header('Content-type: application/json'); header('Content-Length: ' . strlen($response) ); echo $response; } break; // Register request. case 'r': verifyEmptyParameter('email'); if (!in_array((int)$page->settings->getSetting('registerstatus'), [Settings::REGISTER_STATUS_OPEN, Settings::REGISTER_STATUS_API_ONLY])) { showApiError(104); } // Check email is valid format. if (!$page->users->isValidEmail($_GET['email'])) { showApiError(106); } // Check email isn't taken. $ret = $page->users->getByEmail($_GET['email']); if (isset($ret['id'])) { showApiError(105); } // Create username/pass and register. $username = $page->users->generateUsername($_GET['email']); $password = $page->users->generatePassword(); // Register. $userDefault = $page->users->getDefaultRole(); $uid = $page->users->signUp( $username, $password, $_GET['email'], $_SERVER['REMOTE_ADDR'], $userDefault['id'], $userDefault['defaultinvites'] ); // Check if it succeeded. $userData = $page->users->getById($uid); if (!$userData) { showApiError(107); } $response = "\n" . '\n"; header('Content-type: text/xml'); header('Content-Length: ' . strlen($response) ); echo $response; break; } /** * Display error/error code. * @param int $errorCode * @param string $errorText */ function showApiError($errorCode = 900, $errorText = '') { if ($errorText === '') { switch ($errorCode) { case 100: $errorText = 'Incorrect user credentials'; break; case 101: $errorText = 'Account suspended'; break; case 102: $errorText = 'Insufficient privileges/not authorized'; break; case 103: $errorText = 'Registration denied'; break; case 104: $errorText = 'Registrations are closed'; break; case 105: $errorText = 'Invalid registration (Email Address Taken)'; break; case 106: $errorText = 'Invalid registration (Email Address Bad Format)'; break; case 107: $errorText = 'Registration Failed (Data error)'; break; case 200: $errorText = 'Missing parameter'; break; case 201: $errorText = 'Incorrect parameter'; break; case 202: $errorText = 'No such function'; break; case 203: $errorText = 'Function not available'; break; case 300: $errorText = 'No such item'; break; case 500: $errorText = 'Request limit reached'; break; case 501: $errorText = 'Download limit reached'; break; default: $errorText = 'Unknown error'; break; } } $response = "\n" . '\n"; header('Content-type: text/xml'); header('Content-Length: ' . strlen($response) ); header('X-nZEDb: API ERROR [' . $errorCode . '] ' . $errorText); exit($response); } /** * Verify maxage parameter. * @return int */ function maxAge() { $maxAge = -1; if (isset($_GET['maxage'])) { if ($_GET['maxage'] == '') { showApiError(201, 'Incorrect parameter (maxage must not be empty)'); } elseif (!is_numeric($_GET['maxage'])) { showApiError(201, 'Incorrect parameter (maxage must be numeric)'); } else { $maxAge = (int)$_GET['maxage']; } } return $maxAge; } /** * Verify cat parameter. * @return array */ function categoryID() { $categoryID[] = -1; if (isset($_GET['cat'])) { $categoryIDs = urldecode($_GET['cat']); // Append Web-DL category ID if HD present for SickBeard / Sonarr compatibility. if (strpos($categoryIDs, (string)Category::CAT_TV_HD) !== false && strpos($categoryIDs, (string)Category::CAT_TV_WEBDL) === false) { $categoryIDs .= (',' . Category::CAT_TV_WEBDL); } $categoryID = explode(',', $categoryIDs); } return $categoryID; } /** * Verify limit parameter. * @return int */ function limit() { $limit = 100; if (isset($_GET['limit']) && is_numeric($_GET['limit']) && $_GET['limit'] < 100) { $limit = (int)$_GET['limit']; } return $limit; } /** * Verify offset parameter. * @return int */ function offset() { $offset = 0; if (isset($_GET['offset']) && is_numeric($_GET['offset'])) { $offset = (int)$_GET['offset']; } return $offset; } /** * Print XML or JSON output. * @param array $data Data to print. * @param bool $xml True: Print as XML False: Print as JSON. * @param BasePage $page * @param int $offset Offset for limit. */ function printOutput($data, $xml = true, $page, $offset = 0) { if ($xml) { $page->smarty->assign('offset', $offset); $page->smarty->assign('releases', $data); $response = trim($page->smarty->fetch('apiresult.tpl')); header('Content-type: text/xml'); header('Content-Length: ' . strlen($response) ); echo $response; } else { $response = encodeAsJSON($data); header('Content-type: application/json'); header('Content-Length: ' . strlen($response)); echo $response; } } /** * Check if a parameter is empty. * @param string $parameter */ function verifyEmptyParameter($parameter) { if (isset($_GET[$parameter]) && $_GET[$parameter] == '') { showApiError(201, 'Incorrect parameter (' . $parameter . ' must not be empty)'); } } function addCoverURL(&$releases, callable $getCoverURL) { if ($releases && count($releases)) { foreach ($releases as $key => $release) { $coverURL = $getCoverURL($release); $releases[$key]['coverurl'] = $coverURL; } } } /** * Add language from media info XML to release search names. * @param array $releases * @param newznab\db\Settings $settings * @return array */ function addLanguage(&$releases, Settings $settings) { if ($releases && count($releases)) { foreach ($releases as $key => $release) { if (isset($release['id'])) { $language = $settings->queryOneRow(' SELECT audiolanguage FROM releaseaudio WHERE releaseid = ' . $release['id'] ); if ($language !== false) { $releases[$key]['searchname'] = $releases[$key]['searchname'] . ' ' . $language['audiolanguage']; } } } } } function encodeAsJSON($data) { $json = json_encode(Utility::encodeAsUTF8($data)); if ($json === false) { showApiError(201); } return $json; }