organizr-functions.php 84 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693
  1. <?php
  2. function checkPlexAdminFilled()
  3. {
  4. if ($GLOBALS['plexAdmin'] == '') {
  5. return false;
  6. } else {
  7. if ((strpos($GLOBALS['plexAdmin'], '@') !== false)) {
  8. return 'email';
  9. } else {
  10. return 'username';
  11. }
  12. }
  13. }
  14. function organizrSpecialSettings()
  15. {
  16. $refreshSearch = "Refresh";
  17. $tautulliSearch = "tautulli_token";
  18. $tautulli = array_filter($_COOKIE, function ($k) use ($tautulliSearch) {
  19. return stripos($k, $tautulliSearch) !== false;
  20. }, ARRAY_FILTER_USE_KEY);
  21. return array(
  22. 'homepage' => array(
  23. 'refresh' => array_filter($GLOBALS, function ($k) use ($refreshSearch) {
  24. return stripos($k, $refreshSearch) !== false;
  25. }, ARRAY_FILTER_USE_KEY),
  26. 'search' => array(
  27. 'enabled' => (qualifyRequest($GLOBALS['mediaSearchAuth']) && $GLOBALS['mediaSearch'] == true && $GLOBALS['plexToken']) ? true : false,
  28. 'type' => $GLOBALS['mediaSearchType'],
  29. ),
  30. 'ombi' => array(
  31. 'enabled' => (qualifyRequest($GLOBALS['homepageOmbiAuth']) && qualifyRequest($GLOBALS['homepageOmbiRequestAuth']) && $GLOBALS['homepageOmbiEnabled'] == true && $GLOBALS['ssoOmbi'] && isset($_COOKIE['Auth'])) ? true : false,
  32. 'authView' => (qualifyRequest($GLOBALS['homepageOmbiAuth'])) ? true : false,
  33. 'authRequest' => (qualifyRequest($GLOBALS['homepageOmbiRequestAuth'])) ? true : false,
  34. 'sso' => ($GLOBALS['ssoOmbi']) ? true : false,
  35. 'cookie' => (isset($_COOKIE['Auth'])) ? true : false,
  36. 'alias' => ($GLOBALS['ombiAlias']) ? true : false,
  37. 'ombiDefaultFilterAvailable' => $GLOBALS['ombiDefaultFilterAvailable'] ? true : false,
  38. 'ombiDefaultFilterUnavailable' => $GLOBALS['ombiDefaultFilterUnavailable'] ? true : false,
  39. 'ombiDefaultFilterApproved' => $GLOBALS['ombiDefaultFilterApproved'] ? true : false,
  40. 'ombiDefaultFilterUnapproved' => $GLOBALS['ombiDefaultFilterUnapproved'] ? true : false,
  41. 'ombiDefaultFilterDenied' => $GLOBALS['ombiDefaultFilterDenied'] ? true : false
  42. ),
  43. 'options' => array(
  44. 'alternateHomepageHeaders' => $GLOBALS['alternateHomepageHeaders'],
  45. 'healthChecksTags' => $GLOBALS['healthChecksTags'],
  46. 'titles' => array(
  47. 'tautulli' => $GLOBALS['tautulliHeader']
  48. )
  49. ),
  50. 'media' => array(
  51. 'jellyfin' => (strpos($GLOBALS['embyURL'], 'jellyfin') !== false) ? true : false
  52. )
  53. ),
  54. 'sso' => array(
  55. 'misc' => array(
  56. 'oAuthLogin' => isset($_COOKIE['oAuth']) ? true : false,
  57. 'rememberMe' => $GLOBALS['rememberMe'],
  58. 'rememberMeDays' => $GLOBALS['rememberMeDays']
  59. ),
  60. 'plex' => array(
  61. 'enabled' => ($GLOBALS['ssoPlex']) ? true : false,
  62. 'cookie' => isset($_COOKIE['mpt']) ? true : false,
  63. 'machineID' => (strlen($GLOBALS['plexID']) == 40) ? true : false,
  64. 'token' => ($GLOBALS['plexToken'] !== '') ? true : false,
  65. 'plexAdmin' => checkPlexAdminFilled(),
  66. 'strict' => ($GLOBALS['plexStrictFriends']) ? true : false,
  67. 'oAuthEnabled' => ($GLOBALS['plexoAuth']) ? true : false,
  68. 'backend' => ($GLOBALS['authBackend'] == 'plex') ? true : false,
  69. ),
  70. 'ombi' => array(
  71. 'enabled' => ($GLOBALS['ssoOmbi']) ? true : false,
  72. 'cookie' => isset($_COOKIE['Auth']) ? true : false,
  73. 'url' => ($GLOBALS['ombiURL'] !== '') ? $GLOBALS['ombiURL'] : false,
  74. 'api' => ($GLOBALS['ombiToken'] !== '') ? true : false,
  75. ),
  76. 'tautulli' => array(
  77. 'enabled' => ($GLOBALS['ssoTautulli']) ? true : false,
  78. 'cookie' => !empty($tautulli) ? true : false,
  79. 'url' => ($GLOBALS['tautulliURL'] !== '') ? $GLOBALS['tautulliURL'] : false,
  80. ),
  81. ),
  82. 'ping' => array(
  83. 'onlineSound' => $GLOBALS['pingOnlineSound'],
  84. 'offlineSound' => $GLOBALS['pingOfflineSound'],
  85. 'statusSounds' => $GLOBALS['statusSounds'],
  86. 'auth' => $GLOBALS['pingAuth'],
  87. 'authMessage' => $GLOBALS['pingAuthMessage'],
  88. 'authMs' => $GLOBALS['pingAuthMs'],
  89. 'ms' => $GLOBALS['pingMs'],
  90. 'adminRefresh' => $GLOBALS['adminPingRefresh'],
  91. 'everyoneRefresh' => $GLOBALS['otherPingRefresh'],
  92. ),
  93. 'notifications' => array(
  94. 'backbone' => $GLOBALS['notificationBackbone'],
  95. 'position' => $GLOBALS['notificationPosition']
  96. ),
  97. 'lockout' => array(
  98. 'enabled' => $GLOBALS['lockoutSystem'],
  99. 'timer' => $GLOBALS['lockoutTimeout'],
  100. 'minGroup' => $GLOBALS['lockoutMinAuth'],
  101. 'maxGroup' => $GLOBALS['lockoutMaxAuth']
  102. ),
  103. 'user' => array(
  104. 'agent' => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null,
  105. 'oAuthLogin' => isset($_COOKIE['oAuth']) ? true : false,
  106. 'local' => (isLocal()) ? true : false,
  107. 'ip' => userIP()
  108. ),
  109. 'login' => array(
  110. 'rememberMe' => $GLOBALS['rememberMe'],
  111. 'rememberMeDays' => $GLOBALS['rememberMeDays'],
  112. 'wanDomain' => $GLOBALS['wanDomain'],
  113. 'localAddress' => $GLOBALS['localAddress'],
  114. 'enableLocalAddressForward' => $GLOBALS['enableLocalAddressForward'],
  115. ),
  116. 'misc' => array(
  117. 'installedPlugins' => qualifyRequest(1) ? $GLOBALS['installedPlugins'] : '',
  118. 'installedThemes' => qualifyRequest(1) ? $GLOBALS['installedThemes'] : '',
  119. 'return' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : false,
  120. 'authDebug' => $GLOBALS['authDebug'],
  121. 'minimalLoginScreen' => $GLOBALS['minimalLoginScreen'],
  122. 'unsortedTabs' => $GLOBALS['unsortedTabs'],
  123. 'authType' => $GLOBALS['authType'],
  124. 'authBackend' => $GLOBALS['authBackend'],
  125. 'newMessageSound' => (isset($GLOBALS['CHAT-newMessageSound-include'])) ? $GLOBALS['CHAT-newMessageSound-include'] : '',
  126. 'uuid' => $GLOBALS['uuid'],
  127. 'docker' => qualifyRequest(1) ? $GLOBALS['docker'] : '',
  128. 'githubCommit' => qualifyRequest(1) ? $GLOBALS['commit'] : '',
  129. 'schema' => qualifyRequest(1) ? getSchema() : '',
  130. 'debugArea' => qualifyRequest($GLOBALS['debugAreaAuth']),
  131. 'debugErrors' => $GLOBALS['debugErrors'],
  132. 'sandbox' => $GLOBALS['sandbox'],
  133. )
  134. );
  135. }
  136. function wizardConfig($array)
  137. {
  138. foreach ($array['data'] as $items) {
  139. foreach ($items as $key => $value) {
  140. if ($key == 'name') {
  141. $newKey = $value;
  142. }
  143. if ($key == 'value') {
  144. $newValue = $value;
  145. }
  146. if (isset($newKey) && isset($newValue)) {
  147. $$newKey = $newValue;
  148. }
  149. }
  150. }
  151. $location = cleanDirectory($location);
  152. $dbName = dbExtension($dbName);
  153. $configVersion = $GLOBALS['installedVersion'];
  154. $configArray = array(
  155. 'dbName' => $dbName,
  156. 'dbLocation' => $location,
  157. 'license' => $license,
  158. 'organizrHash' => $hashKey,
  159. 'organizrAPI' => $api,
  160. 'registrationPassword' => $registrationPassword,
  161. );
  162. // Create Config
  163. $GLOBALS['dbLocation'] = $location;
  164. $GLOBALS['dbName'] = $dbName;
  165. if (createConfig($configArray)) {
  166. // Call DB Create
  167. if (createDB($location, $dbName)) {
  168. // Add in first user
  169. if (createFirstAdmin($location, $dbName, $username, $password, $email)) {
  170. if (createToken($username, $email, gravatar($email), 'Admin', 0, $hashKey, 1)) {
  171. return true;
  172. } else {
  173. return 'token';
  174. }
  175. } else {
  176. return 'admin';
  177. }
  178. } else {
  179. return 'db';
  180. }
  181. } else {
  182. return 'config';
  183. }
  184. return false;
  185. }
  186. function register($array)
  187. {
  188. // Grab username and password from login form
  189. foreach ($array['data'] as $items) {
  190. foreach ($items as $key => $value) {
  191. if ($key == 'name') {
  192. $newKey = $value;
  193. }
  194. if ($key == 'value') {
  195. $newValue = $value;
  196. }
  197. if (isset($newKey) && isset($newValue)) {
  198. $$newKey = $newValue;
  199. }
  200. }
  201. }
  202. if ($registrationPassword == $GLOBALS['registrationPassword']) {
  203. $defaults = defaultUserGroup();
  204. writeLog('success', 'Registration Function - Registration Password Verified', $username);
  205. if (createUser($username, $password, $defaults, $email)) {
  206. writeLog('success', 'Registration Function - A User has registered', $username);
  207. if (createToken($username, $email, gravatar($email), $defaults['group'], $defaults['group_id'], $GLOBALS['organizrHash'], $GLOBALS['rememberMeDays'])) {
  208. writeLoginLog($username, 'success');
  209. writeLog('success', 'Login Function - A User has logged in', $username);
  210. return true;
  211. }
  212. } else {
  213. writeLog('error', 'Registration Function - An error occured', $username);
  214. return 'username taken';
  215. }
  216. } else {
  217. writeLog('warning', 'Registration Function - Wrong Password', $username);
  218. return 'mismatch';
  219. }
  220. }
  221. function removeFile($array)
  222. {
  223. $filePath = $array['data']['path'];
  224. $fileName = $array['data']['name'];
  225. if (file_exists($filePath)) {
  226. if (unlink($filePath)) {
  227. writeLog('success', 'Log Management Function - Log: ' . $fileName . ' has been purged/deleted', 'SYSTEM');
  228. return true;
  229. } else {
  230. writeLog('error', 'Log Management Function - Log: ' . $fileName . ' - Error Occured', 'SYSTEM');
  231. return false;
  232. }
  233. } else {
  234. writeLog('error', 'Log Management Function - Log: ' . $fileName . ' does not exist', 'SYSTEM');
  235. return false;
  236. }
  237. }
  238. function recover($array)
  239. {
  240. $email = $array['data']['email'];
  241. $newPassword = randString(10);
  242. try {
  243. $connect = new Dibi\Connection([
  244. 'driver' => 'sqlite3',
  245. 'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
  246. ]);
  247. $isUser = $connect->fetch('SELECT * FROM users WHERE email = ? COLLATE NOCASE', $email);
  248. if ($isUser) {
  249. $connect->query('
  250. UPDATE users SET', [
  251. 'password' => password_hash($newPassword, PASSWORD_BCRYPT)
  252. ], '
  253. WHERE email=? COLLATE NOCASE', $email);
  254. if ($GLOBALS['PHPMAILER-enabled']) {
  255. $emailTemplate = array(
  256. 'type' => 'reset',
  257. 'body' => $GLOBALS['PHPMAILER-emailTemplateResetPassword'],
  258. 'subject' => $GLOBALS['PHPMAILER-emailTemplateResetPasswordSubject'],
  259. 'user' => $isUser['username'],
  260. 'password' => $newPassword,
  261. 'inviteCode' => null,
  262. );
  263. $emailTemplate = phpmEmailTemplate($emailTemplate);
  264. $sendEmail = array(
  265. 'to' => $email,
  266. 'user' => $isUser['username'],
  267. 'subject' => $emailTemplate['subject'],
  268. 'body' => phpmBuildEmail($emailTemplate),
  269. );
  270. phpmSendEmail($sendEmail);
  271. }
  272. writeLog('success', 'User Management Function - User: ' . $isUser['username'] . '\'s password was reset', $isUser['username']);
  273. return true;
  274. } else {
  275. writeLog('error', 'User Management Function - Error - User: ' . $email . ' An error Occured', $email);
  276. return 'an error occured';
  277. }
  278. } catch (Dibi\Exception $e) {
  279. writeLog('error', 'User Management Function - Error - User: ' . $email . ' An error Occured', $email);
  280. return 'an error occured';
  281. }
  282. }
  283. function unlock($array)
  284. {
  285. if ($array['data']['password'] == '') {
  286. return 'Password Not Set';
  287. }
  288. try {
  289. $connect = new Dibi\Connection([
  290. 'driver' => 'sqlite3',
  291. 'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
  292. ]);
  293. $result = $connect->fetch('SELECT * FROM users WHERE id = ?', $GLOBALS['organizrUser']['userID']);
  294. if (!password_verify($array['data']['password'], $result['password'])) {
  295. return 'Password Incorrect';
  296. }
  297. $connect->query('
  298. UPDATE users SET', [
  299. 'locked' => ''
  300. ], '
  301. WHERE id=?', $GLOBALS['organizrUser']['userID']);
  302. writeLog('success', 'User Lockout Function - User: ' . $GLOBALS['organizrUser']['username'] . '\'s account unlocked', $GLOBALS['organizrUser']['username']);
  303. return true;
  304. } catch (Dibi\Exception $e) {
  305. writeLog('error', 'User Management Function - Error - User: ' . $GLOBALS['organizrUser']['username'] . ' An error Occured', $GLOBALS['organizrUser']['username']);
  306. return 'an error occured';
  307. }
  308. }
  309. function lock()
  310. {
  311. if ($GLOBALS['organizrUser']['userID'] == '999') {
  312. return 'Not Allowed on Guest';
  313. }
  314. try {
  315. $connect = new Dibi\Connection([
  316. 'driver' => 'sqlite3',
  317. 'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
  318. ]);
  319. $connect->query('
  320. UPDATE users SET', [
  321. 'locked' => '1'
  322. ], '
  323. WHERE id=?', $GLOBALS['organizrUser']['userID']);
  324. writeLog('success', 'User Lockout Function - User: ' . $GLOBALS['organizrUser']['username'] . '\'s account unlocked', $GLOBALS['organizrUser']['username']);
  325. return true;
  326. } catch (Dibi\Exception $e) {
  327. writeLog('error', 'User Management Function - Error - User: ' . $GLOBALS['organizrUser']['username'] . ' An error Occured', $GLOBALS['organizrUser']['username']);
  328. return 'an error occured';
  329. }
  330. }
  331. function editUser($array)
  332. {
  333. if ($array['data']['username'] == '' && $array['data']['username'] == '') {
  334. return 'Username/email not set';
  335. }
  336. try {
  337. $connect = new Dibi\Connection([
  338. 'driver' => 'sqlite3',
  339. 'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],
  340. ]);
  341. if (!usernameTakenExcept($array['data']['username'], $array['data']['email'], $GLOBALS['organizrUser']['userID'])) {
  342. $connect->query('
  343. UPDATE users SET', [
  344. 'username' => $array['data']['username'],
  345. 'email' => $array['data']['email'],
  346. 'image' => gravatar($array['data']['email']),
  347. ], '
  348. WHERE id=?', $GLOBALS['organizrUser']['userID']);
  349. if (!empty($array['data']['password'])) {
  350. $connect->query('
  351. UPDATE users SET', [
  352. 'password' => password_hash($array['data']['password'], PASSWORD_BCRYPT)
  353. ], '
  354. WHERE id=?', $GLOBALS['organizrUser']['userID']);
  355. }
  356. writeLog('success', 'User Management Function - User: ' . $array['data']['username'] . '\'s info was changed', $GLOBALS['organizrUser']['username']);
  357. return true;
  358. } else {
  359. return 'Username/Email Already Taken';
  360. }
  361. } catch (Dibi\Exception $e) {
  362. writeLog('error', 'User Management Function - Error - User: ' . $array['data']['username'] . ' An error Occured', $GLOBALS['organizrUser']['username']);
  363. return 'an error occured';
  364. }
  365. }
  366. function clearTautulliTokens()
  367. {
  368. foreach (array_keys($_COOKIE) as $k => $v) {
  369. if (strpos($v, 'tautulli') !== false) {
  370. coookie('delete', $v);
  371. }
  372. }
  373. }
  374. function logout()
  375. {
  376. coookie('delete', $GLOBALS['cookieName']);
  377. coookie('delete', 'mpt');
  378. coookie('delete', 'Auth');
  379. coookie('delete', 'oAuth');
  380. clearTautulliTokens();
  381. revokeToken(array('data' => array('token' => $GLOBALS['organizrUser']['token'])));
  382. $GLOBALS['organizrUser'] = false;
  383. return true;
  384. }
  385. function qualifyRequest($accessLevelNeeded)
  386. {
  387. if (getUserLevel() <= $accessLevelNeeded && getUserLevel() !== null) {
  388. return true;
  389. } else {
  390. return false;
  391. }
  392. }
  393. function isApprovedRequest($method)
  394. {
  395. $requesterToken = isset(getallheaders()['Token']) ? getallheaders()['Token'] : (isset($_GET['apikey']) ? $_GET['apikey'] : false);
  396. if (isset($_POST['data']['formKey'])) {
  397. $formKey = $_POST['data']['formKey'];
  398. } elseif (isset(getallheaders()['Formkey'])) {
  399. $formKey = getallheaders()['Formkey'];
  400. } elseif (isset(getallheaders()['formkey'])) {
  401. $formKey = getallheaders()['formkey'];
  402. } elseif (isset(getallheaders()['formKey'])) {
  403. $formKey = getallheaders()['formKey'];
  404. } elseif (isset(getallheaders()['FormKey'])) {
  405. $formKey = getallheaders()['FormKey'];
  406. } else {
  407. $formKey = false;
  408. }
  409. // Check token or API key
  410. // If API key, return 0 for admin
  411. if (strlen($requesterToken) == 20 && $requesterToken == $GLOBALS['organizrAPI']) {
  412. //DO API CHECK
  413. return true;
  414. } elseif ($method == 'POST') {
  415. if (checkFormKey($formKey)) {
  416. return true;
  417. } else {
  418. writeLog('error', 'API ERROR: Unable to authenticate Form Key: ' . $formKey, $GLOBALS['organizrUser']['username']);
  419. }
  420. } else {
  421. return true;
  422. }
  423. return false;
  424. }
  425. function getUserLevel()
  426. {
  427. // Grab token
  428. //$requesterToken = isset(getallheaders()['Token']) ? getallheaders()['Token'] : false;
  429. $requesterToken = isset(getallheaders()['Token']) ? getallheaders()['Token'] : (isset($_GET['apikey']) ? $_GET['apikey'] : false);
  430. // Check token or API key
  431. // If API key, return 0 for admin
  432. if (strlen($requesterToken) == 20 && $requesterToken == $GLOBALS['organizrAPI']) {
  433. //DO API CHECK
  434. return 0;
  435. } elseif (isset($GLOBALS['organizrUser'])) {
  436. return $GLOBALS['organizrUser']['groupID'];
  437. }
  438. // All else fails? return guest id
  439. return 999;
  440. }
  441. function organizrStatus()
  442. {
  443. $status = array();
  444. $dependenciesActive = array();
  445. $dependenciesInactive = array();
  446. $extensions = array("PDO_SQLITE", "PDO", "SQLITE3", "zip", "cURL", "openssl", "simplexml", "json", "session", "filter");
  447. $functions = array("hash", "fopen", "fsockopen", "fwrite", "fclose", "readfile");
  448. foreach ($extensions as $check) {
  449. if (extension_loaded($check)) {
  450. array_push($dependenciesActive, $check);
  451. } else {
  452. array_push($dependenciesInactive, $check);
  453. }
  454. }
  455. foreach ($functions as $check) {
  456. if (function_exists($check)) {
  457. array_push($dependenciesActive, $check);
  458. } else {
  459. array_push($dependenciesInactive, $check);
  460. }
  461. }
  462. if (!file_exists('config' . DIRECTORY_SEPARATOR . 'config.php')) {
  463. $status['status'] = "wizard";//wizard - ok for test
  464. }
  465. if (count($dependenciesInactive) > 0 || !is_writable(dirname(__DIR__, 2)) || !(version_compare(PHP_VERSION, $GLOBALS['minimumPHP']) >= 0)) {
  466. $status['status'] = "dependencies";
  467. }
  468. $status['status'] = (!empty($status['status'])) ? $status['status'] : $status['status'] = "ok";
  469. $status['writable'] = is_writable(dirname(__DIR__, 2)) ? 'yes' : 'no';
  470. $status['minVersion'] = (version_compare(PHP_VERSION, $GLOBALS['minimumPHP']) >= 0) ? 'yes' : 'no';
  471. $status['dependenciesActive'] = $dependenciesActive;
  472. $status['dependenciesInactive'] = $dependenciesInactive;
  473. $status['version'] = $GLOBALS['installedVersion'];
  474. $status['os'] = getOS();
  475. $status['php'] = phpversion();
  476. return $status;
  477. }
  478. function pathsWritable($paths)
  479. {
  480. $results = array();
  481. foreach ($paths as $k => $v) {
  482. $results[$k] = is_writable($v);
  483. }
  484. return $results;
  485. }
  486. function getSettingsMain()
  487. {
  488. return array(
  489. 'Github' => array(
  490. array(
  491. 'type' => 'select',
  492. 'name' => 'branch',
  493. 'label' => 'Branch',
  494. 'value' => $GLOBALS['branch'],
  495. 'options' => getBranches(),
  496. 'disabled' => $GLOBALS['docker'],
  497. 'help' => ($GLOBALS['docker']) ? 'Since you are using the Official Docker image, Change the image to change the branch' : 'Choose which branch to download from'
  498. ),
  499. array(
  500. 'type' => 'button',
  501. 'name' => 'force-install-branch',
  502. 'label' => 'Force Install Branch',
  503. 'class' => 'updateNow',
  504. 'icon' => 'fa fa-download',
  505. 'text' => 'Retrieve',
  506. 'attr' => ($GLOBALS['docker']) ? 'title="You can just restart your docker to update"' : '',
  507. 'help' => ($GLOBALS['docker']) ? 'Since you are using the official Docker image, you can just restart your Docker container to update Organizr' : 'This will re-download all of the source files for Organizr'
  508. )
  509. ),
  510. 'API' => array(
  511. array(
  512. 'type' => 'password-alt',
  513. 'name' => 'organizrAPI',
  514. 'label' => 'Organizr API',
  515. 'value' => $GLOBALS['organizrAPI']
  516. ),
  517. array(
  518. 'type' => 'button',
  519. 'label' => 'Generate New API Key',
  520. 'class' => 'newAPIKey',
  521. 'icon' => 'fa fa-refresh',
  522. 'text' => 'Generate'
  523. )
  524. ),
  525. 'Authentication' => array(
  526. array(
  527. 'type' => 'select',
  528. 'name' => 'authType',
  529. 'id' => 'authSelect',
  530. 'label' => 'Authentication Type',
  531. 'value' => $GLOBALS['authType'],
  532. 'options' => getAuthTypes()
  533. ),
  534. array(
  535. 'type' => 'select',
  536. 'name' => 'authBackend',
  537. 'id' => 'authBackendSelect',
  538. 'label' => 'Authentication Backend',
  539. 'class' => 'backendAuth switchAuth',
  540. 'value' => $GLOBALS['authBackend'],
  541. 'options' => getAuthBackends()
  542. ),
  543. array(
  544. 'type' => 'password-alt',
  545. 'name' => 'plexToken',
  546. 'class' => 'plexAuth switchAuth',
  547. 'label' => 'Plex Token',
  548. 'value' => $GLOBALS['plexToken'],
  549. 'placeholder' => 'Use Get Token Button'
  550. ),
  551. array(
  552. 'type' => 'button',
  553. 'label' => 'Get Plex Token',
  554. 'class' => 'popup-with-form getPlexTokenAuth plexAuth switchAuth',
  555. 'icon' => 'fa fa-ticket',
  556. 'text' => 'Retrieve',
  557. 'href' => '#auth-plex-token-form',
  558. 'attr' => 'data-effect="mfp-3d-unfold"'
  559. ),
  560. array(
  561. 'type' => 'password-alt',
  562. 'name' => 'plexID',
  563. 'class' => 'plexAuth switchAuth',
  564. 'label' => 'Plex Machine',
  565. 'value' => $GLOBALS['plexID'],
  566. 'placeholder' => 'Use Get Plex Machine Button'
  567. ),
  568. array(
  569. 'type' => 'button',
  570. 'label' => 'Get Plex Machine',
  571. 'class' => 'popup-with-form getPlexMachineAuth plexAuth switchAuth',
  572. 'icon' => 'fa fa-id-badge',
  573. 'text' => 'Retrieve',
  574. 'href' => '#auth-plex-machine-form',
  575. 'attr' => 'data-effect="mfp-3d-unfold"'
  576. ),
  577. array(
  578. 'type' => 'input',
  579. 'name' => 'plexAdmin',
  580. 'label' => 'Admin Username',
  581. 'class' => 'plexAuth switchAuth',
  582. 'value' => $GLOBALS['plexAdmin'],
  583. 'placeholder' => 'Admin username for Plex'
  584. ),
  585. array(
  586. 'type' => 'switch',
  587. 'name' => 'plexoAuth',
  588. 'label' => 'Enable Plex oAuth',
  589. 'class' => 'plexAuth switchAuth',
  590. 'value' => $GLOBALS['plexoAuth']
  591. ),
  592. array(
  593. 'type' => 'switch',
  594. 'name' => 'plexStrictFriends',
  595. 'label' => 'Strict Plex Friends ',
  596. 'class' => 'plexAuth switchAuth',
  597. 'value' => $GLOBALS['plexStrictFriends'],
  598. 'help' => 'Enabling this will only allow Friends that have shares to the Machine ID entered above to login, Having this disabled will allow all Friends on your Friends list to login'
  599. ),
  600. array(
  601. 'type' => 'switch',
  602. 'name' => 'ignoreTFALocal',
  603. 'label' => 'Ignore External 2FA on Local Subnet',
  604. 'value' => $GLOBALS['ignoreTFALocal'],
  605. 'help' => 'Enabling this will bypass external 2FA security if user is on local Subnet'
  606. ),
  607. array(
  608. 'type' => 'input',
  609. 'name' => 'authBackendHost',
  610. 'class' => 'ldapAuth ftpAuth switchAuth',
  611. 'label' => 'Host Address',
  612. 'value' => $GLOBALS['authBackendHost'],
  613. 'placeholder' => 'http{s) | ftp(s) | ldap(s)://hostname:port'
  614. ),
  615. array(
  616. 'type' => 'input',
  617. 'name' => 'authBaseDN',
  618. 'class' => 'ldapAuth switchAuth',
  619. 'label' => 'Host Base DN',
  620. 'value' => $GLOBALS['authBaseDN'],
  621. 'placeholder' => 'cn=%s,dc=sub,dc=domain,dc=com'
  622. ),
  623. array(
  624. 'type' => 'select',
  625. 'name' => 'ldapType',
  626. 'id' => 'ldapType',
  627. 'label' => 'LDAP Backend Type',
  628. 'class' => 'ldapAuth switchAuth',
  629. 'value' => $GLOBALS['ldapType'],
  630. 'options' => getLDAPOptions()
  631. ),
  632. array(
  633. 'type' => 'input',
  634. 'name' => 'authBackendHostPrefix',
  635. 'class' => 'ldapAuth switchAuth',
  636. 'label' => 'Account Prefix',
  637. 'id' => 'authBackendHostPrefix-input',
  638. 'value' => $GLOBALS['authBackendHostPrefix'],
  639. 'placeholder' => 'Account prefix - i.e. Controller\ from Controller\Username for AD - uid= for OpenLDAP'
  640. ),
  641. array(
  642. 'type' => 'input',
  643. 'name' => 'authBackendHostSuffix',
  644. 'class' => 'ldapAuth switchAuth',
  645. 'label' => 'Account Suffix',
  646. 'id' => 'authBackendHostSuffix-input',
  647. 'value' => $GLOBALS['authBackendHostSuffix'],
  648. 'placeholder' => 'Account suffix - start with comma - ,ou=people,dc=domain,dc=tld'
  649. ),
  650. array(
  651. 'type' => 'input',
  652. 'name' => 'ldapBindUsername',
  653. 'class' => 'ldapAuth switchAuth',
  654. 'label' => 'Bind Username',
  655. 'value' => $GLOBALS['ldapBindUsername'],
  656. 'placeholder' => ''
  657. ),
  658. array(
  659. 'type' => 'password',
  660. 'name' => 'ldapBindPassword',
  661. 'class' => 'ldapAuth switchAuth',
  662. 'label' => 'Password',
  663. 'value' => $GLOBALS['ldapBindPassword']
  664. ),
  665. array(
  666. 'type' => 'html',
  667. 'class' => 'ldapAuth switchAuth',
  668. 'label' => 'Account DN',
  669. 'html' => '<span id="accountDN" class="ldapAuth switchAuth">' . $GLOBALS['authBackendHostPrefix'] . 'TestAcct' . $GLOBALS['authBackendHostSuffix'] . '</span>'
  670. ),
  671. array(
  672. 'type' => 'button',
  673. 'name' => 'test-button-ldap',
  674. 'label' => 'Test Connection',
  675. 'icon' => 'fa fa-flask',
  676. 'class' => 'ldapAuth switchAuth',
  677. 'text' => 'Test Connection',
  678. 'attr' => 'onclick="testAPIConnection(\'ldap\')"',
  679. 'help' => 'Remember! Please save before using the test button!'
  680. ),
  681. array(
  682. 'type' => 'button',
  683. 'name' => 'test-button-ldap-login',
  684. 'label' => 'Test Login',
  685. 'icon' => 'fa fa-flask',
  686. 'class' => 'ldapAuth switchAuth',
  687. 'text' => 'Test Login',
  688. 'attr' => 'onclick="showLDAPLoginTest()"'
  689. ),
  690. array(
  691. 'type' => 'input',
  692. 'name' => 'embyURL',
  693. 'class' => 'embyAuth switchAuth',
  694. 'label' => 'Emby URL',
  695. 'value' => $GLOBALS['embyURL'],
  696. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  697. 'placeholder' => 'http(s)://hostname:port'
  698. ),
  699. array(
  700. 'type' => 'password-alt',
  701. 'name' => 'embyToken',
  702. 'class' => 'embyAuth switchAuth',
  703. 'label' => 'Emby Token',
  704. 'value' => $GLOBALS['embyToken'],
  705. 'placeholder' => ''
  706. ),
  707. /*array(
  708. 'type' => 'button',
  709. 'label' => 'Send Test',
  710. 'class' => 'phpmSendTestEmail',
  711. 'icon' => 'fa fa-paper-plane',
  712. 'text' => 'Send'
  713. )*/
  714. ),
  715. 'Security' => array(
  716. array(
  717. 'type' => 'number',
  718. 'name' => 'loginAttempts',
  719. 'label' => 'Max Login Attempts',
  720. 'value' => $GLOBALS['loginAttempts'],
  721. 'placeholder' => ''
  722. ),
  723. array(
  724. 'type' => 'select',
  725. 'name' => 'loginLockout',
  726. 'label' => 'Login Lockout Seconds',
  727. 'value' => $GLOBALS['loginLockout'],
  728. 'options' => optionTime()
  729. ),
  730. array(
  731. 'type' => 'number',
  732. 'name' => 'lockoutTimeout',
  733. 'label' => 'Inactivity Timer [Minutes]',
  734. 'value' => $GLOBALS['lockoutTimeout'],
  735. 'placeholder' => ''
  736. ),
  737. array(
  738. 'type' => 'select',
  739. 'name' => 'lockoutMinAuth',
  740. 'label' => 'Lockout Groups From',
  741. 'value' => $GLOBALS['lockoutMinAuth'],
  742. 'options' => groupSelect()
  743. ),
  744. array(
  745. 'type' => 'select',
  746. 'name' => 'lockoutMaxAuth',
  747. 'label' => 'Lockout Groups To',
  748. 'value' => $GLOBALS['lockoutMaxAuth'],
  749. 'options' => groupSelect()
  750. ),
  751. array(
  752. 'type' => 'switch',
  753. 'name' => 'lockoutSystem',
  754. 'label' => 'Inactivity Lock',
  755. 'value' => $GLOBALS['lockoutSystem']
  756. ),
  757. array(
  758. 'type' => 'select',
  759. 'name' => 'debugAreaAuth',
  760. 'label' => 'Minimum Authentication for Debug Area',
  761. 'value' => $GLOBALS['debugAreaAuth'],
  762. 'options' => groupSelect()
  763. ),
  764. array(
  765. 'type' => 'switch',
  766. 'name' => 'authDebug',
  767. 'label' => 'Nginx Auth Debug',
  768. 'help' => 'Important! Do not keep this enabled for too long as this opens up Authentication while testing.',
  769. 'value' => $GLOBALS['authDebug'],
  770. 'class' => 'authDebug'
  771. ),
  772. array(
  773. 'type' => 'select2',
  774. 'class' => 'select2-multiple',
  775. 'id' => 'sandbox-select',
  776. 'name' => 'sandbox',
  777. 'label' => 'iFrame Sandbox',
  778. 'value' => $GLOBALS['sandbox'],
  779. 'help' => 'WARNING! This can potentially mess up your iFrames',
  780. 'options' => array(
  781. array(
  782. 'name' => 'Allow Presentation',
  783. 'value' => 'allow-presentation'
  784. ),
  785. array(
  786. 'name' => 'Allow Forms',
  787. 'value' => 'allow-forms'
  788. ),
  789. array(
  790. 'name' => 'Allow Same Origin',
  791. 'value' => 'allow-same-origin'
  792. ),
  793. array(
  794. 'name' => 'Allow Pointer Lock',
  795. 'value' => 'allow-pointer-lock'
  796. ),
  797. array(
  798. 'name' => 'Allow Scripts',
  799. 'value' => 'allow-scripts'
  800. ), array(
  801. 'name' => 'Allow Popups',
  802. 'value' => 'allow-popups'
  803. ),
  804. array(
  805. 'name' => 'Allow Modals',
  806. 'value' => 'allow-modals'
  807. ),
  808. array(
  809. 'name' => 'Allow Top Navigation',
  810. 'value' => 'allow-top-navigation'
  811. ),
  812. )
  813. ),
  814. array(
  815. 'type' => 'switch',
  816. 'name' => 'traefikAuthEnable',
  817. 'label' => 'Enable Traefik Auth Redirect',
  818. 'help' => 'This will enable the webserver to forward errors so traefik will accept them',
  819. 'value' => $GLOBALS['traefikAuthEnable']
  820. ),
  821. ),
  822. 'Performance' => array(
  823. array(
  824. 'type' => 'switch',
  825. 'name' => 'performanceDisableIconDropdown',
  826. 'label' => 'Disable Icon Dropdown',
  827. 'help' => 'Disable select dropdown boxes on new and edit tab forms',
  828. 'value' => $GLOBALS['performanceDisableIconDropdown'],
  829. ),
  830. array(
  831. 'type' => 'switch',
  832. 'name' => 'performanceDisableImageDropdown',
  833. 'label' => 'Disable Image Dropdown',
  834. 'help' => 'Disable select dropdown boxes on new and edit tab forms',
  835. 'value' => $GLOBALS['performanceDisableImageDropdown'],
  836. ),
  837. ),
  838. 'Login' => array(
  839. array(
  840. 'type' => 'password-alt',
  841. 'name' => 'registrationPassword',
  842. 'label' => 'Registration Password',
  843. 'help' => 'Sets the password for the Registration form on the login screen',
  844. 'value' => $GLOBALS['registrationPassword'],
  845. ),
  846. array(
  847. 'type' => 'switch',
  848. 'name' => 'hideRegistration',
  849. 'label' => 'Hide Registration',
  850. 'help' => 'Enable this to hide the Registration button on the login screen',
  851. 'value' => $GLOBALS['hideRegistration'],
  852. ),
  853. array(
  854. 'type' => 'number',
  855. 'name' => 'rememberMeDays',
  856. 'label' => 'Remember Me Length',
  857. 'help' => 'Number of days cookies and tokens will be valid for',
  858. 'value' => $GLOBALS['rememberMeDays'],
  859. 'placeholder' => '',
  860. 'attr' => 'min="1"'
  861. ),
  862. array(
  863. 'type' => 'switch',
  864. 'name' => 'rememberMe',
  865. 'label' => 'Remember Me',
  866. 'help' => 'Default status of Remember Me button on login screen',
  867. 'value' => $GLOBALS['rememberMe'],
  868. ),
  869. array(
  870. 'type' => 'input',
  871. 'name' => 'localIPFrom',
  872. 'label' => 'Override Local IP From',
  873. 'value' => $GLOBALS['localIPFrom'],
  874. 'placeholder' => 'i.e. 123.123.123.123',
  875. 'help' => 'IPv4 only at the moment - This will set your login as local if your IP falls within the From and To'
  876. ),
  877. array(
  878. 'type' => 'input',
  879. 'name' => 'localIPTo',
  880. 'label' => 'Override Local IP To',
  881. 'value' => $GLOBALS['localIPTo'],
  882. 'placeholder' => 'i.e. 123.123.123.123',
  883. 'help' => 'IPv4 only at the moment - This will set your login as local if your IP falls within the From and To'
  884. ),
  885. array(
  886. 'type' => 'input',
  887. 'name' => 'wanDomain',
  888. 'label' => 'WAN Domain',
  889. 'value' => $GLOBALS['wanDomain'],
  890. 'placeholder' => 'only domain and tld - i.e. domain.com',
  891. 'help' => 'Enter domain if you wish to be forwarded to a local address - Local Address filled out on next item'
  892. ),
  893. array(
  894. 'type' => 'input',
  895. 'name' => 'localAddress',
  896. 'label' => 'Local Address',
  897. 'value' => $GLOBALS['localAddress'],
  898. 'placeholder' => 'http://home.local',
  899. 'help' => 'Full local address of organizr install - i.e. http://home.local or http://192.168.0.100'
  900. ),
  901. array(
  902. 'type' => 'switch',
  903. 'name' => 'enableLocalAddressForward',
  904. 'label' => 'Enable Local Address Forward',
  905. 'help' => 'Enables the local address forward if on local address and accessed from WAN Domain',
  906. 'value' => $GLOBALS['enableLocalAddressForward'],
  907. ),
  908. ),
  909. 'Auth Proxy' => array(
  910. array(
  911. 'type' => 'switch',
  912. 'name' => 'authProxyEnabled',
  913. 'label' => 'Auth Proxy',
  914. 'help' => 'Enable option to set Auth Proxy Header Login',
  915. 'value' => $GLOBALS['authProxyEnabled'],
  916. ),
  917. array(
  918. 'type' => 'input',
  919. 'name' => 'authProxyHeaderName',
  920. 'label' => 'Auth Proxy Header Name',
  921. 'value' => $GLOBALS['authProxyHeaderName'],
  922. 'placeholder' => 'i.e. X-Forwarded-User',
  923. 'help' => 'Please choose a unique value for added security'
  924. ),
  925. array(
  926. 'type' => 'input',
  927. 'name' => 'authProxyWhitelist',
  928. 'label' => 'Auth Proxy Whitelist',
  929. 'value' => $GLOBALS['authProxyWhitelist'],
  930. 'placeholder' => 'i.e. 10.0.0.0/24 or 10.0.0.20',
  931. 'help' => 'IPv4 only at the moment - This must be set to work, will accept subnet or IP address'
  932. ),
  933. ),
  934. 'Ping' => array(
  935. array(
  936. 'type' => 'select',
  937. 'name' => 'pingAuth',
  938. 'label' => 'Minimum Authentication',
  939. 'value' => $GLOBALS['pingAuth'],
  940. 'options' => groupSelect()
  941. ),
  942. array(
  943. 'type' => 'select',
  944. 'name' => 'pingAuthMessage',
  945. 'label' => 'Minimum Authentication for Message and Sound',
  946. 'value' => $GLOBALS['pingAuthMessage'],
  947. 'options' => groupSelect()
  948. ),
  949. array(
  950. 'type' => 'select',
  951. 'name' => 'pingOnlineSound',
  952. 'label' => 'Online Sound',
  953. 'value' => $GLOBALS['pingOnlineSound'],
  954. 'options' => getSounds()
  955. ),
  956. array(
  957. 'type' => 'select',
  958. 'name' => 'pingOfflineSound',
  959. 'label' => 'Offline Sound',
  960. 'value' => $GLOBALS['pingOfflineSound'],
  961. 'options' => getSounds()
  962. ),
  963. array(
  964. 'type' => 'switch',
  965. 'name' => 'pingMs',
  966. 'label' => 'Show Ping Time',
  967. 'value' => $GLOBALS['pingMs']
  968. ),
  969. array(
  970. 'type' => 'switch',
  971. 'name' => 'statusSounds',
  972. 'label' => 'Enable Notify Sounds',
  973. 'value' => $GLOBALS['statusSounds'],
  974. 'help' => 'Will play a sound if the server goes down and will play sound if comes back up.',
  975. ),
  976. array(
  977. 'type' => 'select',
  978. 'name' => 'pingAuthMs',
  979. 'label' => 'Minimum Authentication for Time Display',
  980. 'value' => $GLOBALS['pingAuthMs'],
  981. 'options' => groupSelect()
  982. ),
  983. array(
  984. 'type' => 'select',
  985. 'name' => 'adminPingRefresh',
  986. 'label' => 'Admin Refresh Seconds',
  987. 'value' => $GLOBALS['adminPingRefresh'],
  988. 'options' => optionTime()
  989. ),
  990. array(
  991. 'type' => 'select',
  992. 'name' => 'otherPingRefresh',
  993. 'label' => 'Everyone Refresh Seconds',
  994. 'value' => $GLOBALS['otherPingRefresh'],
  995. 'options' => optionTime()
  996. ),
  997. )
  998. );
  999. }
  1000. function getSSO()
  1001. {
  1002. return array(
  1003. 'FYI' => array(
  1004. array(
  1005. 'type' => 'html',
  1006. 'label' => 'Important Information',
  1007. 'override' => 12,
  1008. 'html' => '
  1009. <div class="row">
  1010. <div class="col-lg-12">
  1011. <div class="panel panel-info">
  1012. <div class="panel-heading">
  1013. <span lang="en">Notice</span>
  1014. </div>
  1015. <div class="panel-wrapper collapse in" aria-expanded="true">
  1016. <div class="panel-body">
  1017. <span lang="en">This is not the same as database authentication - i.e. Plex Authentication | Emby Authentication | FTP Authentication<br/>Click Main on the sub-menu above.</span>
  1018. </div>
  1019. </div>
  1020. </div>
  1021. </div>
  1022. </div>
  1023. '
  1024. )
  1025. ),
  1026. 'Plex' => array(
  1027. array(
  1028. 'type' => 'password-alt',
  1029. 'name' => 'plexToken',
  1030. 'label' => 'Plex Token',
  1031. 'value' => $GLOBALS['plexToken'],
  1032. 'placeholder' => 'Use Get Token Button'
  1033. ),
  1034. array(
  1035. 'type' => 'button',
  1036. 'label' => 'Get Plex Token',
  1037. 'class' => 'popup-with-form getPlexTokenSSO',
  1038. 'icon' => 'fa fa-ticket',
  1039. 'text' => 'Retrieve',
  1040. 'href' => '#sso-plex-token-form',
  1041. 'attr' => 'data-effect="mfp-3d-unfold"'
  1042. ),
  1043. array(
  1044. 'type' => 'password-alt',
  1045. 'name' => 'plexID',
  1046. 'label' => 'Plex Machine',
  1047. 'value' => $GLOBALS['plexID'],
  1048. 'placeholder' => 'Use Get Plex Machine Button'
  1049. ),
  1050. array(
  1051. 'type' => 'button',
  1052. 'label' => 'Get Plex Machine',
  1053. 'class' => 'popup-with-form getPlexMachineSSO',
  1054. 'icon' => 'fa fa-id-badge',
  1055. 'text' => 'Retrieve',
  1056. 'href' => '#sso-plex-machine-form',
  1057. 'attr' => 'data-effect="mfp-3d-unfold"'
  1058. ),
  1059. array(
  1060. 'type' => 'input',
  1061. 'name' => 'plexAdmin',
  1062. 'label' => 'Admin Username',
  1063. 'value' => $GLOBALS['plexAdmin'],
  1064. 'placeholder' => 'Admin username for Plex'
  1065. ),
  1066. array(
  1067. 'type' => 'blank',
  1068. 'label' => ''
  1069. ),
  1070. array(
  1071. 'type' => 'html',
  1072. 'label' => 'Plex Note',
  1073. 'html' => '<span lang="en">Please make sure both Token and Machine are filled in</span>'
  1074. ),
  1075. array(
  1076. 'type' => 'switch',
  1077. 'name' => 'ssoPlex',
  1078. 'label' => 'Enable',
  1079. 'value' => $GLOBALS['ssoPlex']
  1080. )
  1081. ),
  1082. 'Tautulli' => array(
  1083. array(
  1084. 'type' => 'input',
  1085. 'name' => 'tautulliURL',
  1086. 'label' => 'Tautulli URL',
  1087. 'value' => $GLOBALS['tautulliURL'],
  1088. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  1089. 'placeholder' => 'http(s)://hostname:port'
  1090. ),
  1091. array(
  1092. 'type' => 'switch',
  1093. 'name' => 'ssoTautulli',
  1094. 'label' => 'Enable',
  1095. 'value' => $GLOBALS['ssoTautulli']
  1096. )
  1097. ),
  1098. 'Ombi' => array(
  1099. array(
  1100. 'type' => 'input',
  1101. 'name' => 'ombiURL',
  1102. 'label' => 'Ombi URL',
  1103. 'value' => $GLOBALS['ombiURL'],
  1104. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  1105. 'placeholder' => 'http(s)://hostname:port'
  1106. ),
  1107. array(
  1108. 'type' => 'password-alt',
  1109. 'name' => 'ombiToken',
  1110. 'label' => 'Token',
  1111. 'value' => $GLOBALS['ombiToken']
  1112. ),
  1113. array(
  1114. 'type' => 'switch',
  1115. 'name' => 'ssoOmbi',
  1116. 'label' => 'Enable',
  1117. 'value' => $GLOBALS['ssoOmbi']
  1118. )
  1119. )
  1120. );
  1121. }
  1122. function loadAppearance()
  1123. {
  1124. $appearance = array();
  1125. $appearance['logo'] = $GLOBALS['logo'];
  1126. $appearance['title'] = $GLOBALS['title'];
  1127. $appearance['useLogo'] = $GLOBALS['useLogo'];
  1128. $appearance['useLogoLogin'] = $GLOBALS['useLogoLogin'];
  1129. $appearance['headerColor'] = $GLOBALS['headerColor'];
  1130. $appearance['headerTextColor'] = $GLOBALS['headerTextColor'];
  1131. $appearance['sidebarColor'] = $GLOBALS['sidebarColor'];
  1132. $appearance['headerTextColor'] = $GLOBALS['headerTextColor'];
  1133. $appearance['sidebarTextColor'] = $GLOBALS['sidebarTextColor'];
  1134. $appearance['accentColor'] = $GLOBALS['accentColor'];
  1135. $appearance['accentTextColor'] = $GLOBALS['accentTextColor'];
  1136. $appearance['buttonColor'] = $GLOBALS['buttonColor'];
  1137. $appearance['buttonTextColor'] = $GLOBALS['buttonTextColor'];
  1138. $appearance['buttonTextHoverColor'] = $GLOBALS['buttonTextHoverColor'];
  1139. $appearance['buttonHoverColor'] = $GLOBALS['buttonHoverColor'];
  1140. $appearance['loginWallpaper'] = $GLOBALS['loginWallpaper'];
  1141. $appearance['loginLogo'] = $GLOBALS['loginLogo'];
  1142. $appearance['customCss'] = $GLOBALS['customCss'];
  1143. $appearance['customThemeCss'] = $GLOBALS['customThemeCss'];
  1144. $appearance['customJava'] = $GLOBALS['customJava'];
  1145. $appearance['customThemeJava'] = $GLOBALS['customThemeJava'];
  1146. return $appearance;
  1147. }
  1148. function getCustomizeAppearance()
  1149. {
  1150. if (file_exists(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php')) {
  1151. return array(
  1152. 'Top Bar' => array(
  1153. array(
  1154. 'type' => 'input',
  1155. 'name' => 'logo',
  1156. 'label' => 'Logo',
  1157. 'value' => $GLOBALS['logo']
  1158. ),
  1159. array(
  1160. 'type' => 'input',
  1161. 'name' => 'title',
  1162. 'label' => 'Title',
  1163. 'value' => $GLOBALS['title']
  1164. ),
  1165. array(
  1166. 'type' => 'switch',
  1167. 'name' => 'useLogo',
  1168. 'label' => 'Use Logo instead of Title',
  1169. 'value' => $GLOBALS['useLogo'],
  1170. 'help' => 'Also sets the title of your site'
  1171. ),
  1172. array(
  1173. 'type' => 'input',
  1174. 'name' => 'description',
  1175. 'label' => 'Meta Description',
  1176. 'value' => $GLOBALS['description'],
  1177. 'help' => 'Used to set the description for SEO meta tags'
  1178. ),
  1179. ),
  1180. 'Login Page' => array(
  1181. array(
  1182. 'type' => 'input',
  1183. 'name' => 'loginLogo',
  1184. 'label' => 'Login Logo',
  1185. 'value' => $GLOBALS['loginLogo'],
  1186. ),
  1187. array(
  1188. 'type' => 'input',
  1189. 'name' => 'loginWallpaper',
  1190. 'label' => 'Login Wallpaper',
  1191. 'value' => $GLOBALS['loginWallpaper'],
  1192. 'help' => 'You may enter multiple URL\'s using the CSV format. i.e. link#1,link#2,link#3'
  1193. ),
  1194. array(
  1195. 'type' => 'switch',
  1196. 'name' => 'useLogoLogin',
  1197. 'label' => 'Use Logo instead of Title on Login Page',
  1198. 'value' => $GLOBALS['useLogoLogin']
  1199. ),
  1200. array(
  1201. 'type' => 'switch',
  1202. 'name' => 'minimalLoginScreen',
  1203. 'label' => 'Minimal Login Screen',
  1204. 'value' => $GLOBALS['minimalLoginScreen']
  1205. )
  1206. ),
  1207. 'Options' => array(
  1208. array(
  1209. 'type' => 'switch',
  1210. 'name' => 'alternateHomepageHeaders',
  1211. 'label' => 'Alternate Homepage Titles',
  1212. 'value' => $GLOBALS['alternateHomepageHeaders']
  1213. ),
  1214. array(
  1215. 'type' => 'switch',
  1216. 'name' => 'debugErrors',
  1217. 'label' => 'Show Debug Errors',
  1218. 'value' => $GLOBALS['debugErrors']
  1219. ),
  1220. array(
  1221. 'type' => 'select',
  1222. 'name' => 'unsortedTabs',
  1223. 'label' => 'Unsorted Tab Placement',
  1224. 'value' => $GLOBALS['unsortedTabs'],
  1225. 'options' => array(
  1226. array(
  1227. 'name' => 'Top',
  1228. 'value' => 'top'
  1229. ),
  1230. array(
  1231. 'name' => 'Bottom',
  1232. 'value' => 'bottom'
  1233. )
  1234. )
  1235. ),
  1236. array(
  1237. 'type' => 'input',
  1238. 'name' => 'gaTrackingID',
  1239. 'label' => 'Google Analytics Tracking ID',
  1240. 'placeholder' => 'e.g. UA-XXXXXXXXX-X',
  1241. 'value' => $GLOBALS['gaTrackingID']
  1242. )
  1243. ),
  1244. 'Colors & Themes' => array(
  1245. array(
  1246. 'type' => 'html',
  1247. 'override' => 12,
  1248. 'label' => 'Custom CSS [Can replace colors from above]',
  1249. 'html' => '
  1250. <div class="row">
  1251. <div class="col-lg-12">
  1252. <div class="panel panel-info">
  1253. <div class="panel-heading">
  1254. <span lang="en">Notice</span>
  1255. </div>
  1256. <div class="panel-wrapper collapse in" aria-expanded="true">
  1257. <div class="panel-body">
  1258. <span lang="en">The value of #987654 is just a placeholder, you can change to any value you like.</span>
  1259. <span lang="en">To revert back to default, save with no value defined in the relevant field.</span>
  1260. </div>
  1261. </div>
  1262. </div>
  1263. </div>
  1264. </div>
  1265. ',
  1266. ),
  1267. array(
  1268. 'type' => 'blank',
  1269. 'label' => ''
  1270. ),
  1271. array(
  1272. 'type' => 'input',
  1273. 'name' => 'headerColor',
  1274. 'label' => 'Nav Bar Color',
  1275. 'value' => $GLOBALS['headerColor'],
  1276. 'class' => 'pick-a-color',
  1277. 'attr' => 'data-original="' . $GLOBALS['headerColor'] . '"'
  1278. ),
  1279. array(
  1280. 'type' => 'input',
  1281. 'name' => 'headerTextColor',
  1282. 'label' => 'Nav Bar Text Color',
  1283. 'value' => $GLOBALS['headerTextColor'],
  1284. 'class' => 'pick-a-color',
  1285. 'attr' => 'data-original="' . $GLOBALS['headerTextColor'] . '"'
  1286. ),
  1287. array(
  1288. 'type' => 'input',
  1289. 'name' => 'sidebarColor',
  1290. 'label' => 'Side Bar Color',
  1291. 'value' => $GLOBALS['sidebarColor'],
  1292. 'class' => 'pick-a-color',
  1293. 'attr' => 'data-original="' . $GLOBALS['sidebarColor'] . '"'
  1294. ),
  1295. array(
  1296. 'type' => 'input',
  1297. 'name' => 'sidebarTextColor',
  1298. 'label' => 'Side Bar Text Color',
  1299. 'value' => $GLOBALS['sidebarTextColor'],
  1300. 'class' => 'pick-a-color',
  1301. 'attr' => 'data-original="' . $GLOBALS['sidebarTextColor'] . '"'
  1302. ),
  1303. array(
  1304. 'type' => 'input',
  1305. 'name' => 'accentColor',
  1306. 'label' => 'Accent Color',
  1307. 'value' => $GLOBALS['accentColor'],
  1308. 'class' => 'pick-a-color',
  1309. 'attr' => 'data-original="' . $GLOBALS['accentColor'] . '"'
  1310. ),
  1311. array(
  1312. 'type' => 'input',
  1313. 'name' => 'accentTextColor',
  1314. 'label' => 'Accent Text Color',
  1315. 'value' => $GLOBALS['accentTextColor'],
  1316. 'class' => 'pick-a-color',
  1317. 'attr' => 'data-original="' . $GLOBALS['accentTextColor'] . '"'
  1318. ),
  1319. array(
  1320. 'type' => 'input',
  1321. 'name' => 'buttonColor',
  1322. 'label' => 'Button Color',
  1323. 'value' => $GLOBALS['buttonColor'],
  1324. 'class' => 'pick-a-color',
  1325. 'attr' => 'data-original="' . $GLOBALS['buttonColor'] . '"'
  1326. ),
  1327. array(
  1328. 'type' => 'input',
  1329. 'name' => 'buttonTextColor',
  1330. 'label' => 'Button Text Color',
  1331. 'value' => $GLOBALS['buttonTextColor'],
  1332. 'class' => 'pick-a-color',
  1333. 'attr' => 'data-original="' . $GLOBALS['buttonTextColor'] . '"'
  1334. ),/*
  1335. array(
  1336. 'type' => 'input',
  1337. 'name' => 'buttonHoverColor',
  1338. 'label' => 'Button Hover Color',
  1339. 'value' => $GLOBALS['buttonHoverColor'],
  1340. 'class' => 'pick-a-color',
  1341. 'disabled' => true
  1342. ),
  1343. array(
  1344. 'type' => 'input',
  1345. 'name' => 'buttonTextHoverColor',
  1346. 'label' => 'Button Hover Text Color',
  1347. 'value' => $GLOBALS['buttonTextHoverColor'],
  1348. 'class' => 'pick-a-color',
  1349. 'disabled' => true
  1350. ),*/
  1351. array(
  1352. 'type' => 'select',
  1353. 'name' => 'theme',
  1354. 'label' => 'Theme',
  1355. 'class' => 'themeChanger',
  1356. 'value' => $GLOBALS['theme'],
  1357. 'options' => getThemes()
  1358. ),
  1359. array(
  1360. 'type' => 'select',
  1361. 'name' => 'style',
  1362. 'label' => 'Style',
  1363. 'class' => 'styleChanger',
  1364. 'value' => $GLOBALS['style'],
  1365. 'options' => array(
  1366. array(
  1367. 'name' => 'Light',
  1368. 'value' => 'light'
  1369. ),
  1370. array(
  1371. 'name' => 'Dark',
  1372. 'value' => 'dark'
  1373. ),
  1374. array(
  1375. 'name' => 'Horizontal',
  1376. 'value' => 'horizontal'
  1377. )
  1378. )
  1379. )
  1380. ),
  1381. 'Notifications' => array(
  1382. array(
  1383. 'type' => 'select',
  1384. 'name' => 'notificationBackbone',
  1385. 'class' => 'notifyChanger',
  1386. 'label' => 'Type',
  1387. 'value' => $GLOBALS['notificationBackbone'],
  1388. 'options' => optionNotificationTypes()
  1389. ),
  1390. array(
  1391. 'type' => 'select',
  1392. 'name' => 'notificationPosition',
  1393. 'class' => 'notifyPositionChanger',
  1394. 'label' => 'Position',
  1395. 'value' => $GLOBALS['notificationPosition'],
  1396. 'options' => optionNotificationPositions()
  1397. ),
  1398. array(
  1399. 'type' => 'html',
  1400. 'label' => 'Test Message',
  1401. 'html' => '
  1402. <div class="btn-group m-r-10 dropup">
  1403. <button aria-expanded="false" data-toggle="dropdown" class="btn btn-info btn-outline dropdown-toggle waves-effect waves-light" type="button">
  1404. <i class="fa fa-comment m-r-5"></i>
  1405. <span>Test </span>
  1406. </button>
  1407. <ul role="menu" class="dropdown-menu">
  1408. <li><a onclick="message(\'Test Message\',\'This is a success Message\',activeInfo.settings.notifications.position,\'#FFF\',\'success\',\'5000\');">Success</a></li>
  1409. <li><a onclick="message(\'Test Message\',\'This is a info Message\',activeInfo.settings.notifications.position,\'#FFF\',\'info\',\'5000\');">Info</a></li>
  1410. <li><a onclick="message(\'Test Message\',\'This is a warning Message\',activeInfo.settings.notifications.position,\'#FFF\',\'warning\',\'5000\');">Warning</a></li>
  1411. <li><a onclick="message(\'Test Message\',\'This is a error Message\',activeInfo.settings.notifications.position,\'#FFF\',\'error\',\'5000\');">Error</a></li>
  1412. </ul>
  1413. </div>
  1414. '
  1415. )
  1416. ),
  1417. 'FavIcon' => array(
  1418. array(
  1419. 'type' => 'textbox',
  1420. 'name' => 'favIcon',
  1421. 'class' => '',
  1422. 'label' => 'Fav Icon Code',
  1423. 'value' => $GLOBALS['favIcon'],
  1424. 'placeholder' => 'Paste Contents from https://realfavicongenerator.net/',
  1425. 'attr' => 'rows="10"',
  1426. ),
  1427. array(
  1428. 'type' => 'html',
  1429. 'label' => 'Instructions',
  1430. 'html' => '
  1431. <div class="panel panel-default">
  1432. <div class="panel-heading">
  1433. <a href="https://realfavicongenerator.net/" target="_blank"><span class="label label-info m-l-5">Visit FavIcon Site</span></a>
  1434. </div>
  1435. <div class="panel-wrapper collapse in">
  1436. <div class="panel-body">
  1437. <ul class="list-icons">
  1438. <li lang="en"><i class="fa fa-caret-right text-info"></i> Click [Select your Favicon picture]</li>
  1439. <li lang="en"><i class="fa fa-caret-right text-info"></i> Choose your image to use</li>
  1440. <li lang="en"><i class="fa fa-caret-right text-info"></i> Edit settings to your liking</li>
  1441. <li lang="en"><i class="fa fa-caret-right text-info"></i> At bottom of page on [Favicon Generator Options] under [Path] choose [I cannot or I do not want to place favicon files at the root of my web site.]</li>
  1442. <li lang="en"><i class="fa fa-caret-right text-info"></i> Enter this path <code>plugins/images/faviconCustom</code></li>
  1443. <li lang="en"><i class="fa fa-caret-right text-info"></i> Click [Generate your Favicons and HTML code]</li>
  1444. <li lang="en"><i class="fa fa-caret-right text-info"></i> Download and unzip file and place in <code>plugins/images/faviconCustom</code></li>
  1445. <li lang="en"><i class="fa fa-caret-right text-info"></i> Copy code and paste inside left box</li>
  1446. </ul>
  1447. </div>
  1448. </div>
  1449. </div>
  1450. '
  1451. ),
  1452. ),
  1453. 'Custom CSS' => array(
  1454. array(
  1455. 'type' => 'html',
  1456. 'override' => 12,
  1457. 'label' => 'Custom CSS [Can replace colors from above]',
  1458. 'html' => '<button type="button" class="hidden saveCss btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customCSSEditor" style="height:300px">' . htmlentities($GLOBALS['customCss']) . '</div>'
  1459. ),
  1460. array(
  1461. 'type' => 'textbox',
  1462. 'name' => 'customCss',
  1463. 'class' => 'hidden cssTextarea',
  1464. 'label' => '',
  1465. 'value' => $GLOBALS['customCss'],
  1466. 'placeholder' => 'No <style> tags needed',
  1467. 'attr' => 'rows="10"',
  1468. ),
  1469. ),
  1470. 'Theme CSS' => array(
  1471. array(
  1472. 'type' => 'html',
  1473. 'override' => 12,
  1474. 'label' => 'Theme CSS [Can replace colors from above]',
  1475. 'html' => '<button type="button" class="hidden saveCssTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeCSSEditor" style="height:300px">' . htmlentities($GLOBALS['customThemeCss']) . '</div>'
  1476. ),
  1477. array(
  1478. 'type' => 'textbox',
  1479. 'name' => 'customThemeCss',
  1480. 'class' => 'hidden cssThemeTextarea',
  1481. 'label' => '',
  1482. 'value' => $GLOBALS['customThemeCss'],
  1483. 'placeholder' => 'No <style> tags needed',
  1484. 'attr' => 'rows="10"',
  1485. ),
  1486. ),
  1487. 'Custom Javascript' => array(
  1488. array(
  1489. 'type' => 'html',
  1490. 'override' => 12,
  1491. 'label' => 'Custom Javascript',
  1492. 'html' => '<button type="button" class="hidden saveJava btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customJavaEditor" style="height:300px">' . htmlentities($GLOBALS['customJava']) . '</div>'
  1493. ),
  1494. array(
  1495. 'type' => 'textbox',
  1496. 'name' => 'customJava',
  1497. 'class' => 'hidden javaTextarea',
  1498. 'label' => '',
  1499. 'value' => $GLOBALS['customJava'],
  1500. 'placeholder' => 'No <script> tags needed',
  1501. 'attr' => 'rows="10"',
  1502. ),
  1503. ),
  1504. 'Theme Javascript' => array(
  1505. array(
  1506. 'type' => 'html',
  1507. 'override' => 12,
  1508. 'label' => 'Theme Javascript',
  1509. 'html' => '<button type="button" class="hidden saveJavaTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeJavaEditor" style="height:300px">' . htmlentities($GLOBALS['customThemeJava']) . '</div>'
  1510. ),
  1511. array(
  1512. 'type' => 'textbox',
  1513. 'name' => 'customThemeJava',
  1514. 'class' => 'hidden javaThemeTextarea',
  1515. 'label' => '',
  1516. 'value' => $GLOBALS['customThemeJava'],
  1517. 'placeholder' => 'No <script> tags needed',
  1518. 'attr' => 'rows="10"',
  1519. ),
  1520. ),
  1521. );
  1522. }
  1523. }
  1524. function editAppearance($array)
  1525. {
  1526. switch ($array['data']['value']) {
  1527. case 'true':
  1528. $array['data']['value'] = (bool)true;
  1529. break;
  1530. case 'false':
  1531. $array['data']['value'] = (bool)false;
  1532. break;
  1533. default:
  1534. $array['data']['value'] = $array['data']['value'];
  1535. }
  1536. //return gettype($array['data']['value']).' - '.$array['data']['value'];
  1537. switch ($array['data']['action']) {
  1538. case 'editCustomizeAppearance':
  1539. $newItem = array(
  1540. $array['data']['name'] => $array['data']['value']
  1541. );
  1542. return (updateConfig($newItem)) ? true : false;
  1543. break;
  1544. default:
  1545. # code...
  1546. break;
  1547. }
  1548. }
  1549. function updateConfigMultiple($array)
  1550. {
  1551. return (updateConfig($array['data']['payload'])) ? true : false;
  1552. }
  1553. function updateConfigMultipleForm($array)
  1554. {
  1555. $newItem = array();
  1556. foreach ($array['data']['payload'] as $k => $v) {
  1557. $v['value'] = $v['value'] ?? '';
  1558. switch ($v['value']) {
  1559. case 'true':
  1560. $v['value'] = (bool)true;
  1561. break;
  1562. case 'false':
  1563. $v['value'] = (bool)false;
  1564. break;
  1565. default:
  1566. $v['value'] = $v['value'];
  1567. }
  1568. // Hash
  1569. if ($v['type'] == 'password') {
  1570. if (isEncrypted($v['value']) || $v['value'] == '') {
  1571. $v['value'] = $v['value'];
  1572. } else {
  1573. $v['value'] = encrypt($v['value']);
  1574. }
  1575. }
  1576. $newItem[$v['name']] = $v['value'];
  1577. }
  1578. //return $newItem;
  1579. return (updateConfig($newItem)) ? true : false;
  1580. }
  1581. function updateConfigItem($array)
  1582. {
  1583. $array['data']['value'] = $array['data']['value'] ?? '';
  1584. switch ($array['data']['value']) {
  1585. case 'true':
  1586. $array['data']['value'] = (bool)true;
  1587. break;
  1588. case 'false':
  1589. $array['data']['value'] = (bool)false;
  1590. break;
  1591. default:
  1592. $array['data']['value'] = $array['data']['value'];
  1593. }
  1594. // Hash
  1595. if ($array['data']['type'] == 'password') {
  1596. $array['data']['value'] = encrypt($array['data']['value']);
  1597. }
  1598. $newItem = array(
  1599. $array['data']['name'] => $array['data']['value']
  1600. );
  1601. return (updateConfig($newItem)) ? true : false;
  1602. }
  1603. function getPlugins()
  1604. {
  1605. if (file_exists(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php')) {
  1606. $pluginList = [];
  1607. foreach ($GLOBALS['plugins'] as $plugin) {
  1608. foreach ($plugin as $key => $value) {
  1609. if (strpos($value['license'], $GLOBALS['license']) !== false) {
  1610. $plugin[$key]['enabled'] = $GLOBALS[$value['configPrefix'] . '-enabled'];
  1611. $pluginList[$key] = $plugin[$key];
  1612. }
  1613. }
  1614. }
  1615. return $pluginList;
  1616. }
  1617. return false;
  1618. }
  1619. function editPlugins($array)
  1620. {
  1621. switch ($array['data']['action']) {
  1622. case 'enable':
  1623. $newItem = array(
  1624. $array['data']['configName'] => true
  1625. );
  1626. writeLog('success', 'Plugin Function - Enabled Plugin [' . $_POST['data']['name'] . ']', $GLOBALS['organizrUser']['username']);
  1627. return (updateConfig($newItem)) ? true : false;
  1628. break;
  1629. case 'disable':
  1630. $newItem = array(
  1631. $array['data']['configName'] => false
  1632. );
  1633. writeLog('success', 'Plugin Function - Disabled Plugin [' . $_POST['data']['name'] . ']', $GLOBALS['organizrUser']['username']);
  1634. return (updateConfig($newItem)) ? true : false;
  1635. break;
  1636. default:
  1637. # code...
  1638. break;
  1639. }
  1640. }
  1641. function auth()
  1642. {
  1643. $debug = $GLOBALS['authDebug']; // CAREFUL WHEN SETTING TO TRUE AS THIS OPENS AUTH UP
  1644. $ban = isset($_GET['ban']) ? strtoupper($_GET['ban']) : "";
  1645. $whitelist = isset($_GET['whitelist']) ? $_GET['whitelist'] : false;
  1646. $blacklist = isset($_GET['blacklist']) ? $_GET['blacklist'] : false;
  1647. $group = 0;
  1648. $groupParam = $_GET['group'];
  1649. $redirect = false;
  1650. if (isset($groupParam)) {
  1651. if (is_numeric($groupParam)) {
  1652. $group = (int)$groupParam;
  1653. } else {
  1654. $group = getTabGroup($groupParam);
  1655. }
  1656. }
  1657. $currentIP = userIP();
  1658. $unlocked = ($GLOBALS['organizrUser']['locked'] == '1') ? false : true;
  1659. if (isset($GLOBALS['organizrUser'])) {
  1660. $currentUser = $GLOBALS['organizrUser']['username'];
  1661. $currentGroup = $GLOBALS['organizrUser']['groupID'];
  1662. $currentEmail = $GLOBALS['organizrUser']['email'];
  1663. } else {
  1664. $currentUser = 'Guest';
  1665. $currentGroup = getUserLevel();
  1666. $currentEmail = 'guest@guest.com';
  1667. }
  1668. $userInfo = "User: $currentUser | Group: $currentGroup | IP: $currentIP | Requesting Access to Group $group | Result: ";
  1669. if ($whitelist) {
  1670. if (in_array($currentIP, arrayIP($whitelist))) {
  1671. !$debug ? exit(http_response_code(200)) : die("$userInfo Whitelist Authorized");
  1672. }
  1673. }
  1674. if ($blacklist) {
  1675. if (in_array($currentIP, arrayIP($blacklist))) {
  1676. !$debug ? exit(http_response_code(401)) : die("$userInfo Blacklisted");
  1677. }
  1678. }
  1679. if ($group !== null) {
  1680. if ((isset($_SERVER['HTTP_X_FORWARDED_SERVER']) && $_SERVER['HTTP_X_FORWARDED_SERVER'] == 'traefik') || $GLOBALS['traefikAuthEnable']) {
  1681. $redirect = 'Location: ' . getServerPath();
  1682. }
  1683. if (qualifyRequest($group) && $unlocked) {
  1684. header("X-Organizr-User: $currentUser");
  1685. header("X-Organizr-Email: $currentEmail");
  1686. !$debug ? exit(http_response_code(200)) : die("$userInfo Authorized");
  1687. } else {
  1688. !$debug ? (!$redirect ? exit(http_response_code(401)) : exit(http_response_code(401) . header($redirect))) : die("$userInfo Not Authorized");
  1689. }
  1690. } else {
  1691. !$debug ? (!$redirect ? exit(http_response_code(401)) : exit(http_response_code(401) . header($redirect))) : die("Not Authorized Due To No Parameters Set");
  1692. }
  1693. }
  1694. function getTabGroup($tab)
  1695. {
  1696. try {
  1697. $connect = new Dibi\Connection([
  1698. 'driver' => 'sqlite3',
  1699. 'database' => $GLOBALS['dbLocation'] . $GLOBALS['dbName'],]);
  1700. $row = $connect->fetch('SELECT group_id FROM tabs WHERE name LIKE %~like~', $tab);
  1701. return $row ? $row['group_id'] : 0;
  1702. } catch (\Dibi\Exception $e) {
  1703. writeLog('error', 'Tab Group Function - Error Fetching Tab Group', $tab);
  1704. return 0;
  1705. }
  1706. }
  1707. function logoOrText()
  1708. {
  1709. if ($GLOBALS['useLogoLogin'] == false) {
  1710. return '<h1>' . $GLOBALS['title'] . '</h1>';
  1711. } else {
  1712. return '<img class="loginLogo" src="' . $GLOBALS['loginLogo'] . '" alt="Home" />';
  1713. }
  1714. }
  1715. function showLogin()
  1716. {
  1717. if ($GLOBALS['hideRegistration'] == false) {
  1718. return '<p><span lang="en">Don\'t have an account?</span><a href="#" class="text-primary m-l-5 to-register"><b lang="en">Sign Up</b></a></p>';
  1719. }
  1720. }
  1721. function checkoAuth()
  1722. {
  1723. return ($GLOBALS['plexoAuth'] && $GLOBALS['authType'] !== 'internal') ? true : false;
  1724. }
  1725. function checkoAuthOnly()
  1726. {
  1727. return ($GLOBALS['plexoAuth'] && $GLOBALS['authType'] == 'external') ? true : false;
  1728. }
  1729. function showoAuth()
  1730. {
  1731. $buttons = '';
  1732. if ($GLOBALS['plexoAuth'] && $GLOBALS['authType'] !== 'internal') {
  1733. $buttons .= '<a href="javascript:void(0)" onclick="oAuthStart(\'plex\')" class="btn btn-lg btn-block text-uppercase waves-effect waves-light bg-plex text-muted" data-toggle="tooltip" title="" data-original-title="Login with Plex"> <span>Login</span><i aria-hidden="true" class="mdi mdi-plex m-l-5"></i> </a>';
  1734. }
  1735. return ($buttons) ? '
  1736. <div class="panel">
  1737. <div class="panel-heading bg-org" id="plex-login-heading" role="tab">
  1738. <a class="panel-title" data-toggle="collapse" href="#plex-login-collapse" data-parent="#login-panels" aria-expanded="false" aria-controls="organizr-login-collapse">
  1739. <img class="lazyload loginTitle" data-src="plugins/images/tabs/plex.png"> &nbsp;
  1740. <span class="text-uppercase fw300" lang="en">Login with Plex</span>
  1741. </a>
  1742. </div>
  1743. <div class="panel-collapse collapse in" id="plex-login-collapse" aria-labelledby="plex-login-heading" role="tabpanel">
  1744. <div class="panel-body">
  1745. <div class="row">
  1746. <div class="col-xs-12 col-sm-12 col-md-12 text-center">
  1747. <div class="social m-b-0">' . $buttons . '</div>
  1748. </div>
  1749. </div>
  1750. </div>
  1751. </div>
  1752. </div>
  1753. ' : '';
  1754. }
  1755. function getImages()
  1756. {
  1757. $dirname = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'tabs' . DIRECTORY_SEPARATOR;
  1758. $path = 'plugins/images/tabs/';
  1759. $images = scandir($dirname);
  1760. $ignore = array(".", "..", "._.DS_Store", ".DS_Store", ".pydio_id");
  1761. $allIcons = array();
  1762. foreach ($images as $image) {
  1763. if (!in_array($image, $ignore)) {
  1764. $allIcons[] = $path . $image;
  1765. }
  1766. }
  1767. return $allIcons;
  1768. }
  1769. function imageSelect($form)
  1770. {
  1771. $i = 1;
  1772. $images = getImages();
  1773. $return = '<select class="form-control tabIconImageList" id="' . $form . '-chooseImage" name="chooseImage"><option lang="en">Select or type Icon</option>';
  1774. foreach ($images as $image) {
  1775. $i++;
  1776. $return .= '<option value="' . $image . '">' . basename($image) . '</option>';
  1777. }
  1778. return $return . '</select>';
  1779. }
  1780. function editImages()
  1781. {
  1782. $array = array();
  1783. $postCheck = array_filter($_POST);
  1784. $filesCheck = array_filter($_FILES);
  1785. $approvedPath = 'plugins/images/tabs/';
  1786. if (!empty($postCheck)) {
  1787. $removeImage = $approvedPath . pathinfo($_POST['data']['imagePath'], PATHINFO_BASENAME);
  1788. if ($_POST['data']['action'] == 'deleteImage' && approvedFileExtension($removeImage)) {
  1789. if (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $removeImage)) {
  1790. writeLog('success', 'Image Manager Function - Deleted Image [' . $_POST['data']['imageName'] . ']', $GLOBALS['organizrUser']['username']);
  1791. return (unlink(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $removeImage)) ? true : false;
  1792. }
  1793. }
  1794. }
  1795. if (!empty($filesCheck) && approvedFileExtension($_FILES['file']['name']) && strpos($_FILES['file']['type'], 'image/') !== false) {
  1796. ini_set('upload_max_filesize', '10M');
  1797. ini_set('post_max_size', '10M');
  1798. $tempFile = $_FILES['file']['tmp_name'];
  1799. $targetPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'tabs' . DIRECTORY_SEPARATOR;
  1800. $targetFile = $targetPath . $_FILES['file']['name'];
  1801. return (move_uploaded_file($tempFile, $targetFile)) ? true : false;
  1802. }
  1803. return false;
  1804. }
  1805. function approvedFileExtension($filename)
  1806. {
  1807. $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
  1808. switch ($ext) {
  1809. case 'gif':
  1810. case 'png':
  1811. case 'jpeg':
  1812. case 'jpg':
  1813. case 'svg':
  1814. return true;
  1815. break;
  1816. default:
  1817. return false;
  1818. }
  1819. }
  1820. function getThemes()
  1821. {
  1822. $themes = array();
  1823. foreach (glob(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'css' . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . "*.css") as $filename) {
  1824. $themes[] = array(
  1825. 'name' => preg_replace('/\\.[^.\\s]{3,4}$/', '', basename($filename)),
  1826. 'value' => preg_replace('/\\.[^.\\s]{3,4}$/', '', basename($filename))
  1827. );
  1828. }
  1829. return $themes;
  1830. }
  1831. function getSounds()
  1832. {
  1833. $sounds = array();
  1834. foreach (glob(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'sounds' . DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . "*.mp3") as $filename) {
  1835. $sounds[] = array(
  1836. 'name' => preg_replace('/\\.[^.\\s]{3,4}$/', '', basename($filename)),
  1837. 'value' => preg_replace('/\\.[^.\\s]{3,4}$/', '', 'plugins/sounds/default/' . basename($filename) . '.mp3')
  1838. );
  1839. }
  1840. foreach (glob(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'sounds' . DIRECTORY_SEPARATOR . 'custom' . DIRECTORY_SEPARATOR . "*.mp3") as $filename) {
  1841. $sounds[] = array(
  1842. 'name' => preg_replace('/\\.[^.\\s]{3,4}$/', '', basename($filename)),
  1843. 'value' => preg_replace('/\\.[^.\\s]{3,4}$/', '', 'plugins/sounds/custom/' . basename($filename) . '.mp3')
  1844. );
  1845. }
  1846. return $sounds;
  1847. }
  1848. function getBranches()
  1849. {
  1850. return array(
  1851. array(
  1852. 'name' => 'Develop',
  1853. 'value' => 'v2-develop'
  1854. ),
  1855. array(
  1856. 'name' => 'Master',
  1857. 'value' => 'v2-master'
  1858. )
  1859. );
  1860. }
  1861. function getAuthTypes()
  1862. {
  1863. return array(
  1864. array(
  1865. 'name' => 'Organizr DB',
  1866. 'value' => 'internal'
  1867. ),
  1868. array(
  1869. 'name' => 'Organizr DB + Backend',
  1870. 'value' => 'both'
  1871. ),
  1872. array(
  1873. 'name' => 'Backend Only',
  1874. 'value' => 'external'
  1875. )
  1876. );
  1877. }
  1878. function getLDAPOptions()
  1879. {
  1880. return array(
  1881. array(
  1882. 'name' => 'Active Directory',
  1883. 'value' => '1'
  1884. ),
  1885. array(
  1886. 'name' => 'OpenLDAP',
  1887. 'value' => '2'
  1888. ),
  1889. array(
  1890. 'name' => 'First IPA',
  1891. 'value' => '3'
  1892. ),
  1893. );
  1894. }
  1895. function getAuthBackends()
  1896. {
  1897. $backendOptions = array();
  1898. $backendOptions[] = array(
  1899. 'name' => 'Choose Backend',
  1900. 'value' => false,
  1901. 'disabled' => true
  1902. );
  1903. foreach (array_filter(get_defined_functions()['user'], function ($v) {
  1904. return strpos($v, 'plugin_auth_') === 0;
  1905. }) as $value) {
  1906. $name = str_replace('plugin_auth_', '', $value);
  1907. if (strpos($name, 'disabled') === false) {
  1908. $backendOptions[] = array(
  1909. 'name' => ucwords(str_replace('_', ' ', $name)),
  1910. 'value' => $name
  1911. );
  1912. } else {
  1913. $backendOptions[] = array(
  1914. 'name' => $value(),
  1915. 'value' => 'none',
  1916. 'disabled' => true,
  1917. );
  1918. }
  1919. }
  1920. ksort($backendOptions);
  1921. return $backendOptions;
  1922. }
  1923. function wizardPath($array)
  1924. {
  1925. $path = $array['data']['path'];
  1926. if (file_exists($path)) {
  1927. if (is_writable($path)) {
  1928. return true;
  1929. }
  1930. } else {
  1931. if (is_writable(dirname($path, 1))) {
  1932. if (mkdir($path, 0760, true)) {
  1933. return true;
  1934. }
  1935. }
  1936. }
  1937. return 'permissions';
  1938. }
  1939. function groupSelect()
  1940. {
  1941. $groups = allGroups();
  1942. $select = array();
  1943. foreach ($groups as $key => $value) {
  1944. $select[] = array(
  1945. 'name' => $value['group'],
  1946. 'value' => $value['group_id']
  1947. );
  1948. }
  1949. return $select;
  1950. }
  1951. function getImage()
  1952. {
  1953. $refresh = false;
  1954. $cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
  1955. if (!file_exists($cacheDirectory)) {
  1956. mkdir($cacheDirectory, 0777, true);
  1957. }
  1958. @$image_url = $_GET['img'];
  1959. @$key = $_GET['key'];
  1960. @$image_height = $_GET['height'];
  1961. @$image_width = $_GET['width'];
  1962. @$source = $_GET['source'];
  1963. @$itemType = $_GET['type'];
  1964. if (strpos($key, '$') !== false) {
  1965. $key = explode('$', $key)[0];
  1966. $refresh = true;
  1967. }
  1968. switch ($source) {
  1969. case 'plex':
  1970. $plexAddress = qualifyURL($GLOBALS['plexURL']);
  1971. $image_src = $plexAddress . '/photo/:/transcode?height=' . $image_height . '&width=' . $image_width . '&upscale=1&url=' . $image_url . '&X-Plex-Token=' . $GLOBALS['plexToken'];
  1972. break;
  1973. case 'emby':
  1974. $embyAddress = qualifyURL($GLOBALS['embyURL']);
  1975. $imgParams = array();
  1976. if (isset($_GET['height'])) {
  1977. $imgParams['height'] = 'maxHeight=' . $_GET['height'];
  1978. }
  1979. if (isset($_GET['width'])) {
  1980. $imgParams['width'] = 'maxWidth=' . $_GET['width'];
  1981. }
  1982. $image_src = $embyAddress . '/Items/' . $image_url . '/Images/' . $itemType . '?' . implode('&', $imgParams);
  1983. break;
  1984. default:
  1985. # code...
  1986. break;
  1987. }
  1988. if (isset($image_url) && isset($image_height) && isset($image_width) && isset($image_src)) {
  1989. $cachefile = $cacheDirectory . $key . '.jpg';
  1990. $cachetime = 604800;
  1991. // Serve from the cache if it is younger than $cachetime
  1992. if (file_exists($cachefile) && time() - $cachetime < filemtime($cachefile) && $refresh == false) {
  1993. header("Content-type: image/jpeg");
  1994. //@readfile($cachefile);
  1995. //echo @curl('get', $cachefile)['content'];
  1996. $options = array('verify' => false);
  1997. $response = Requests::get($cachefile, array(), $options);
  1998. if ($response->success) {
  1999. echo $response->body;
  2000. }
  2001. exit;
  2002. }
  2003. ob_start(); // Start the output buffer
  2004. header('Content-type: image/jpeg');
  2005. //@readfile($image_src);
  2006. //echo @curl('get', $image_src)['content'];
  2007. $options = array('verify' => false);
  2008. $response = Requests::get($image_src, array(), $options);
  2009. if ($response->success) {
  2010. echo $response->body;
  2011. }
  2012. // Cache the output to a file
  2013. $fp = fopen($cachefile, 'wb');
  2014. fwrite($fp, ob_get_contents());
  2015. fclose($fp);
  2016. ob_end_flush(); // Send the output to the browser
  2017. die();
  2018. } else {
  2019. die("Invalid Request");
  2020. }
  2021. }
  2022. function cacheImage($url, $name, $extension = 'jpg')
  2023. {
  2024. $cacheDirectory = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR;
  2025. if (!file_exists($cacheDirectory)) {
  2026. mkdir($cacheDirectory, 0777, true);
  2027. }
  2028. $cachefile = $cacheDirectory . $name . '.' . $extension;
  2029. $cachetime = 604800;
  2030. if ((file_exists($cachefile) && time() - $cachetime < filemtime($cachefile)) || !file_exists($cachefile)) {
  2031. @copy($url, $cachefile);
  2032. }
  2033. }
  2034. function downloader($array)
  2035. {
  2036. switch ($array['data']['source']) {
  2037. case 'sabnzbd':
  2038. switch ($array['data']['action']) {
  2039. case 'resume':
  2040. case 'pause':
  2041. sabnzbdAction($array['data']['action'], $array['data']['target']);
  2042. break;
  2043. default:
  2044. # code...
  2045. break;
  2046. }
  2047. break;
  2048. case 'jdownloader':
  2049. switch ($array['data']['action']) {
  2050. case 'start':
  2051. jdownloaderAction($array['data']['action'], $array['data']['target']);
  2052. break;
  2053. case 'stop':
  2054. jdownloaderAction($array['data']['action'], $array['data']['target']);
  2055. break;
  2056. case 'resume':
  2057. jdownloaderAction($array['data']['action'], $array['data']['target']);
  2058. break;
  2059. case 'pause':
  2060. jdownloaderAction($array['data']['action'], $array['data']['target']);
  2061. break;
  2062. case 'update':
  2063. jdownloaderAction($array['data']['action'], $array['data']['target']);
  2064. break;
  2065. case 'retry':
  2066. jdownloaderAction($array['data']['action'], $array['data']['target']);
  2067. break;
  2068. case 'remove':
  2069. jdownloaderAction($array['data']['action'], $array['data']['target']);
  2070. break;
  2071. default:
  2072. # code...
  2073. break;
  2074. }
  2075. break;
  2076. case 'nzbget':
  2077. break;
  2078. default:
  2079. # code...
  2080. break;
  2081. }
  2082. }
  2083. function jdownloaderAction($action = null, $target = null)
  2084. {
  2085. if ($GLOBALS['homepageJdownloaderEnabled'] && !empty($GLOBALS['jdownloaderURL']) && qualifyRequest($GLOBALS['homepageJdownloaderAuth'])) {
  2086. $url = qualifyURL($GLOBALS['jdownloaderURL']);
  2087. # This ensures compatibility with RSScrawler
  2088. $url = str_replace('/myjd', '', $url);
  2089. if (substr($url, -1) == '/') {
  2090. $url = substr_replace($url, "", -1);
  2091. }
  2092. switch ($action) {
  2093. case 'start':
  2094. $url = $url . '/myjd_start/';
  2095. break;
  2096. case 'stop':
  2097. $url = $url . '/myjd_stop/';
  2098. break;
  2099. case 'resume':
  2100. $url = $url . '/myjd_pause/false';
  2101. break;
  2102. case 'pause':
  2103. $url = $url . '/myjd_pause/true';
  2104. break;
  2105. case 'update':
  2106. $url = $url . '/myjd_update';
  2107. break;
  2108. case 'retry':
  2109. # code...
  2110. break;
  2111. case 'remove':
  2112. # code...
  2113. break;
  2114. default:
  2115. # code...
  2116. break;
  2117. }
  2118. try {
  2119. $options = (localURL($url)) ? array('verify' => false) : array();
  2120. $response = Requests::post($url, array(), $options);
  2121. if ($response->success) {
  2122. $api['content'] = json_decode($response->body, true);
  2123. }
  2124. } catch (Requests_Exception $e) {
  2125. writeLog('error', 'JDownloader Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2126. };
  2127. $api['content'] = isset($api['content']) ? $api['content'] : false;
  2128. return $api;
  2129. }
  2130. }
  2131. function sabnzbdAction($action = null, $target = null)
  2132. {
  2133. if ($GLOBALS['homepageSabnzbdEnabled'] && !empty($GLOBALS['sabnzbdURL']) && !empty($GLOBALS['sabnzbdToken']) && qualifyRequest($GLOBALS['homepageSabnzbdAuth'])) {
  2134. $url = qualifyURL($GLOBALS['sabnzbdURL']);
  2135. switch ($action) {
  2136. case 'pause':
  2137. $id = ($target !== '' && $target !== 'main' && isset($target)) ? 'mode=queue&name=pause&value=' . $target . '&' : 'mode=pause';
  2138. $url = $url . '/api?' . $id . '&output=json&apikey=' . $GLOBALS['sabnzbdToken'];
  2139. break;
  2140. case 'resume':
  2141. $id = ($target !== '' && $target !== 'main' && isset($target)) ? 'mode=queue&name=resume&value=' . $target . '&' : 'mode=resume';
  2142. $url = $url . '/api?' . $id . '&output=json&apikey=' . $GLOBALS['sabnzbdToken'];
  2143. break;
  2144. default:
  2145. # code...
  2146. break;
  2147. }
  2148. try {
  2149. $options = (localURL($url)) ? array('verify' => false) : array();
  2150. $response = Requests::get($url, array(), $options);
  2151. if ($response->success) {
  2152. $api['content'] = json_decode($response->body, true);
  2153. }
  2154. } catch (Requests_Exception $e) {
  2155. writeLog('error', 'SabNZBd Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2156. };
  2157. $api['content'] = isset($api['content']) ? $api['content'] : false;
  2158. return $api;
  2159. }
  2160. }
  2161. // Deluge API isn't working ATM - will get with dev.
  2162. function delugeAction($action = null, $target = null)
  2163. {
  2164. if ($GLOBALS['homepageDelugeEnabled'] && !empty($GLOBALS['delugeURL']) && !empty($GLOBALS['delugePassword']) && qualifyRequest($GLOBALS['homepageDelugeAuth'])) {
  2165. $url = qualifyURL($GLOBALS['delugeURL']);
  2166. try {
  2167. $deluge = new deluge($GLOBALS['delugeURL'], decrypt($GLOBALS['delugePassword']));
  2168. switch ($action) {
  2169. case 'pause':
  2170. $torrents = $deluge->pauseTorrent($target);
  2171. break;
  2172. case 'pauseAll':
  2173. $torrents = $deluge->pauseAllTorrents();
  2174. break;
  2175. case 'resume':
  2176. $torrents = $deluge->resumeTorrent($target);
  2177. break;
  2178. case 'resumeAll':
  2179. $torrents = $deluge->resumeAllTorrents();
  2180. break;
  2181. default:
  2182. # code...
  2183. break;
  2184. }
  2185. $api['content'] = $torrents;
  2186. } catch (Excecption $e) {
  2187. writeLog('error', 'Deluge Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2188. }
  2189. $api['content'] = isset($api['content']) ? $api['content'] : false;
  2190. return $api;
  2191. }
  2192. return false;
  2193. }
  2194. function getOrgUsers()
  2195. {
  2196. $result = allUsers();
  2197. if (is_array($result) || is_object($result)) {
  2198. foreach ($result['users'] as $k => $v) {
  2199. $return[$v['username']] = $v['email'];
  2200. }
  2201. return $return;
  2202. }
  2203. }
  2204. function convertPlexName($user, $type)
  2205. {
  2206. $array = userList('plex');
  2207. switch ($type) {
  2208. case "username":
  2209. case "u":
  2210. $plexUser = array_search($user, $array['users']);
  2211. break;
  2212. case "id":
  2213. if (array_key_exists(strtolower($user), $array['users'])) {
  2214. $plexUser = $array['users'][strtolower($user)];
  2215. }
  2216. break;
  2217. default:
  2218. $plexUser = false;
  2219. }
  2220. return (!empty($plexUser) ? $plexUser : null);
  2221. }
  2222. function userList($type = null)
  2223. {
  2224. switch ($type) {
  2225. case 'plex':
  2226. if (!empty($GLOBALS['plexToken']) && !empty($GLOBALS['plexID'])) {
  2227. $url = 'https://plex.tv/api/servers/' . $GLOBALS['plexID'] . '/shared_servers';
  2228. try {
  2229. $headers = array(
  2230. "Accept" => "application/json",
  2231. "X-Plex-Token" => $GLOBALS['plexToken']
  2232. );
  2233. $response = Requests::get($url, $headers, array());
  2234. libxml_use_internal_errors(true);
  2235. if ($response->success) {
  2236. $libraryList = array();
  2237. $plex = simplexml_load_string($response->body);
  2238. foreach ($plex->SharedServer as $child) {
  2239. if (!empty($child['username'])) {
  2240. $username = (string)strtolower($child['username']);
  2241. $email = (string)strtolower($child['email']);
  2242. $libraryList['users'][$username] = (string)$child['id'];
  2243. $libraryList['emails'][$email] = (string)$child['id'];
  2244. $libraryList['both'][$username] = $email;
  2245. }
  2246. }
  2247. $libraryList = array_change_key_case($libraryList, CASE_LOWER);
  2248. return $libraryList;
  2249. }
  2250. } catch (Requests_Exception $e) {
  2251. writeLog('error', 'Plex Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2252. };
  2253. }
  2254. break;
  2255. default:
  2256. # code...
  2257. break;
  2258. }
  2259. return false;
  2260. }
  2261. function libraryList($type = null)
  2262. {
  2263. switch ($type) {
  2264. case 'plex':
  2265. if (!empty($GLOBALS['plexToken']) && !empty($GLOBALS['plexID'])) {
  2266. $url = 'https://plex.tv/api/servers/' . $GLOBALS['plexID'];
  2267. try {
  2268. $headers = array(
  2269. "Accept" => "application/json",
  2270. "X-Plex-Token" => $GLOBALS['plexToken']
  2271. );
  2272. $response = Requests::get($url, $headers, array());
  2273. libxml_use_internal_errors(true);
  2274. if ($response->success) {
  2275. $libraryList = array();
  2276. $plex = simplexml_load_string($response->body);
  2277. foreach ($plex->Server->Section as $child) {
  2278. $libraryList['libraries'][(string)$child['title']] = (string)$child['id'];
  2279. }
  2280. $libraryList = array_change_key_case($libraryList, CASE_LOWER);
  2281. return $libraryList;
  2282. }
  2283. } catch (Requests_Exception $e) {
  2284. writeLog('error', 'Plex Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2285. };
  2286. }
  2287. break;
  2288. default:
  2289. # code...
  2290. break;
  2291. }
  2292. return false;
  2293. }
  2294. function plexJoinAPI($array)
  2295. {
  2296. return plexJoin($array['data']['username'], $array['data']['email'], $array['data']['password']);
  2297. }
  2298. function embyJoinAPI($array)
  2299. {
  2300. return embyJoin($array['data']['username'], $array['data']['email'], $array['data']['password']);
  2301. }
  2302. function plexJoin($username, $email, $password)
  2303. {
  2304. try {
  2305. $url = 'https://plex.tv/users.json';
  2306. $headers = array(
  2307. 'Accept' => 'application/json',
  2308. 'Content-Type' => 'application/x-www-form-urlencoded',
  2309. 'X-Plex-Product' => 'Organizr',
  2310. 'X-Plex-Version' => '2.0',
  2311. 'X-Plex-Client-Identifier' => $GLOBALS['uuid'],
  2312. );
  2313. $data = array(
  2314. 'user[email]' => $email,
  2315. 'user[username]' => $username,
  2316. 'user[password]' => $password,
  2317. );
  2318. $response = Requests::post($url, $headers, $data, array());
  2319. $json = json_decode($response->body, true);
  2320. $errors = (!empty($json['errors']) ? true : false);
  2321. $success = (!empty($json['user']) ? true : false);
  2322. //Use This for later
  2323. $usernameError = (!empty($json['errors']['username']) ? $json['errors']['username'][0] : false);
  2324. $emailError = (!empty($json['errors']['email']) ? $json['errors']['email'][0] : false);
  2325. $passwordError = (!empty($json['errors']['password']) ? $json['errors']['password'][0] : false);
  2326. $errorMessage = "";
  2327. if ($errors) {
  2328. if ($usernameError) {
  2329. $errorMessage .= "[Username Error: " . $usernameError . "]";
  2330. }
  2331. if ($emailError) {
  2332. $errorMessage .= "[Email Error: " . $emailError . "]";
  2333. }
  2334. if ($passwordError) {
  2335. $errorMessage .= "[Password Error: " . $passwordError . "]";
  2336. }
  2337. }
  2338. return (!empty($success) && empty($errors) ? true : $errorMessage);
  2339. } catch (Requests_Exception $e) {
  2340. writeLog('error', 'Plex.TV Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2341. };
  2342. return false;
  2343. }
  2344. function embyJoin($username, $email, $password)
  2345. {
  2346. try {
  2347. #create user in emby.
  2348. $headers = array(
  2349. "Accept" => "application/json"
  2350. );
  2351. $data = array();
  2352. $url = $GLOBALS['embyURL'] . '/emby/Users/New?name=' . $username . '&api_key=' . $GLOBALS['embyToken'];
  2353. $response = Requests::Post($url, $headers, json_encode($data), array());
  2354. $response = $response->body;
  2355. //return($response);
  2356. $response = json_decode($response, true);
  2357. //return($response);
  2358. $userID = $response["Id"];
  2359. //return($userID);
  2360. #authenticate as user to update password.
  2361. //randomizer four digits of DeviceId
  2362. // I dont think ther would be security problems with hardcoding deviceID but randomizing it would mitigate any issue.
  2363. $deviceIdSeceret = rand(0, 9) . "" . rand(0, 9) . "" . rand(0, 9) . "" . rand(0, 9);
  2364. //hardcoded device id with the first three digits random 0-9,0-9,0-9,0-9
  2365. $embyAuthHeader = 'MediaBrowser Client="Emby Mobile", Device="Firefox", DeviceId="' . $deviceIdSeceret . 'aWxssS81LgAggFdpbmRvd3MgTlQgMTAuMDsgV2luNjxx7IHf2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzcyLjAuMzYyNi4xMTkgU2FmYXJpLzUzNy4zNnwxNTUxNTczMTAyNDI4", Version="4.0.2.0"';
  2366. $headers = array(
  2367. "Accept" => "application/json",
  2368. "Content-Type" => "application/json",
  2369. "X-Emby-Authorization" => $embyAuthHeader
  2370. );
  2371. $data = array(
  2372. "Pw" => "",
  2373. "Username" => $username
  2374. );
  2375. $url = $GLOBALS['embyURL'] . '/emby/Users/AuthenticateByName';
  2376. $response = Requests::Post($url, $headers, json_encode($data), array());
  2377. $response = $response->body;
  2378. $response = json_decode($response, true);
  2379. $userToken = $response["AccessToken"];
  2380. #update password
  2381. $embyAuthHeader = 'MediaBrowser Client="Emby Mobile", Device="Firefox", Token="' . $userToken . '", DeviceId="' . $deviceIdSeceret . 'aWxssS81LgAggFdpbmRvd3MgTlQgMTAuMDsgV2luNjxx7IHf2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzcyLjAuMzYyNi4xMTkgU2FmYXJpLzUzNy4zNnwxNTUxNTczMTAyNDI4", Version="4.0.2.0"';
  2382. $headers = array(
  2383. "Accept" => "application/json",
  2384. "Content-Type" => "application/json",
  2385. "X-Emby-Authorization" => $embyAuthHeader
  2386. );
  2387. $data = array(
  2388. "CurrentPw" => "",
  2389. "NewPw" => $password,
  2390. "Id" => $userID
  2391. );
  2392. $url = $GLOBALS['embyURL'] . '/emby/Users/' . $userID . '/Password';
  2393. Requests::Post($url, $headers, json_encode($data), array());
  2394. #update config
  2395. $headers = array(
  2396. "Accept" => "application/json",
  2397. "Content-Type" => "application/json"
  2398. );
  2399. $url = $GLOBALS['embyURL'] . '/emby/Users/' . $userID . '/Policy?api_key=' . $GLOBALS['embyToken'];
  2400. $response = Requests::Post($url, $headers, getEmbyTemplateUserJson(), array());
  2401. #add emby.media
  2402. try {
  2403. #seperate because this is not required
  2404. $headers = array(
  2405. "Accept" => "application/json",
  2406. "X-Emby-Authorization" => $embyAuthHeader
  2407. );
  2408. $data = array(
  2409. "ConnectUsername " => $email
  2410. );
  2411. $url = $GLOBALS['embyURL'] . '/emby/Users/' . $userID . '/Connect/Link';
  2412. Requests::Post($url, $headers, json_encode($data), array());
  2413. } catch (Requests_Exception $e) {
  2414. writeLog('error', 'Emby Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2415. }
  2416. return (true);
  2417. //return( "USERID:".$userID);
  2418. } catch (Requests_Exception $e) {
  2419. writeLog('error', 'Emby create Function - Error: ' . $e->getMessage(), 'SYSTEM');
  2420. };
  2421. return false;
  2422. }
  2423. function checkFrame($array, $url)
  2424. {
  2425. if (array_key_exists("x-frame-options", $array)) {
  2426. if ($array['x-frame-options'] == "deny") {
  2427. return false;
  2428. } elseif ($array['x-frame-options'] == "sameorgin") {
  2429. $digest = parse_url($url);
  2430. $host = (isset($digest['host']) ? $digest['host'] : '');
  2431. if (getServer() == $host) {
  2432. return true;
  2433. } else {
  2434. return false;
  2435. }
  2436. }
  2437. } else {
  2438. if (!$array) {
  2439. return false;
  2440. }
  2441. return true;
  2442. }
  2443. }
  2444. /*loads users from emby and returns a correctly formated policy for a new user.
  2445. */
  2446. function getEmbyTemplateUserJson()
  2447. {
  2448. $headers = array(
  2449. "Accept" => "application/json"
  2450. );
  2451. $data = array();
  2452. $url = $GLOBALS['embyURL'] . '/emby/Users?api_key=' . $GLOBALS['embyToken'];
  2453. $response = Requests::Get($url, $headers, array());
  2454. $response = $response->body;
  2455. $response = json_decode($response, true);
  2456. //error_Log("response ".json_encode($response));
  2457. writeLog('error', 'userList:' . json_encode($response), 'SYSTEM');
  2458. //$correct stores the template users object
  2459. $correct = null;
  2460. foreach ($response as $element) {
  2461. if ($element['Name'] == $GLOBALS['INVITES-EmbyTemplate']) {
  2462. $correct = $element;
  2463. }
  2464. }
  2465. writeLog('error', 'Correct user:' . json_encode($correct), 'SYSTEM');
  2466. if ($correct == null) {
  2467. //return empty JSON if user incorectly configured template
  2468. return "{}";
  2469. }
  2470. //select policy section and remove possibly dangeours rows.
  2471. $policy = $correct['Policy'];
  2472. //writeLog('error', 'policy update'.$policy, 'SYSTEM');
  2473. unset($policy['AuthenticationProviderId']);
  2474. unset($policy['InvalidLoginAttemptCount']);
  2475. unset($policy['DisablePremiumFeatures']);
  2476. unset($policy['DisablePremiumFeatures']);
  2477. return (json_encode($policy));
  2478. }
  2479. function frameTest($url)
  2480. {
  2481. $array = array_change_key_case(get_headers(qualifyURL($url), 1));
  2482. $url = qualifyURL($url);
  2483. if (checkFrame($array, $url)) {
  2484. return true;
  2485. } else {
  2486. return false;
  2487. }
  2488. }
  2489. function ping($pings)
  2490. {
  2491. if (qualifyRequest($GLOBALS['pingAuth'])) {
  2492. $type = gettype($pings);
  2493. $ping = new Ping("");
  2494. $ping->setTtl(128);
  2495. $ping->setTimeout(2);
  2496. switch ($type) {
  2497. case "array":
  2498. $results = [];
  2499. foreach ($pings as $k => $v) {
  2500. if (strpos($v, ':') !== false) {
  2501. $domain = explode(':', $v)[0];
  2502. $port = explode(':', $v)[1];
  2503. $ping->setHost($domain);
  2504. $ping->setPort($port);
  2505. $latency = $ping->ping('fsockopen');
  2506. } else {
  2507. $ping->setHost($v);
  2508. $latency = $ping->ping();
  2509. }
  2510. if ($latency || $latency === 0) {
  2511. $results[$v] = $latency;
  2512. } else {
  2513. $results[$v] = false;
  2514. }
  2515. }
  2516. break;
  2517. case "string":
  2518. if (strpos($pings, ':') !== false) {
  2519. $domain = explode(':', $pings)[0];
  2520. $port = explode(':', $pings)[1];
  2521. $ping->setHost($domain);
  2522. $ping->setPort($port);
  2523. $latency = $ping->ping('fsockopen');
  2524. } else {
  2525. $ping->setHost($pings);
  2526. $latency = $ping->ping();
  2527. }
  2528. if ($latency || $latency === 0) {
  2529. $results = $latency;
  2530. } else {
  2531. $results = false;
  2532. }
  2533. break;
  2534. }
  2535. return $results;
  2536. }
  2537. return false;
  2538. }
  2539. function guestHash($start, $end)
  2540. {
  2541. $ip = $_SERVER['REMOTE_ADDR'];
  2542. $ip = md5($ip);
  2543. return substr($ip, $start, $end);
  2544. }
  2545. function importUserButtons()
  2546. {
  2547. $emptyButtons = '
  2548. <div class="col-md-12">
  2549. <div class="white-box bg-org">
  2550. <h3 class="box-title m-0" lang="en">Currently User import is available for Plex only.</h3> </div>
  2551. </div>
  2552. ';
  2553. $buttons = '';
  2554. if (!empty($GLOBALS['plexToken'])) {
  2555. $buttons .= '<button class="btn bg-plex text-muted waves-effect waves-light importUsersButton" onclick="importUsers(\'plex\')" type="button"><span class="btn-label"><i class="mdi mdi-plex"></i></span><span lang="en">Import Plex Users</span></button>';
  2556. }
  2557. return ($buttons !== '') ? $buttons : $emptyButtons;
  2558. }
  2559. function settingsDocker()
  2560. {
  2561. $type = ($GLOBALS['docker']) ? 'Official Docker' : 'Native';
  2562. return '<li><div class="bg-info"><i class="mdi mdi-flag mdi-24px text-white"></i></div><span class="text-muted hidden-xs m-t-10" lang="en">Install Type</span> ' . $type . '</li>';
  2563. }
  2564. function settingsPathChecks()
  2565. {
  2566. $items = '';
  2567. $type = (array_search(false, pathsWritable($GLOBALS['paths']))) ? 'Not Writable' : 'Writable';
  2568. $result = '<li class="mouse" onclick="toggleWritableFolders();"><div class="bg-info"><i class="mdi mdi-folder mdi-24px text-white"></i></div><span class="text-muted hidden-xs m-t-10" lang="en">Organizr Paths</span> ' . $type . '</li>';
  2569. foreach (pathsWritable($GLOBALS['paths']) as $k => $v) {
  2570. $items .= '<li class="folders-writable hidden"><div class="bg-info"><i class="mdi mdi-folder mdi-24px text-white"></i></div><span class="text-muted hidden-xs m-t-10" lang="en">' . $k . '</span> ' . (($v) ? 'Writable' : 'Not Writable') . '</li>';
  2571. }
  2572. return $result . $items;
  2573. }
  2574. function dockerUpdate()
  2575. {
  2576. chdir('/etc/cont-init.d/');
  2577. $dockerUpdate = shell_exec('./30-install');
  2578. return $dockerUpdate;
  2579. }
  2580. function windowsUpdate()
  2581. {
  2582. $branch = ($GLOBALS['branch'] == 'v2-master') ? '-m' : '-d';
  2583. ini_set('max_execution_time', 0);
  2584. set_time_limit(0);
  2585. $logFile = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'log.txt';
  2586. $windowsScript = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'windows-update.bat ' . $branch . ' > ' . $logFile . ' 2>&1';
  2587. $windowsUpdate = shell_exec($windowsScript);
  2588. return ($windowsUpdate) ? $windowsUpdate : 'Update Complete - check log.txt for output';
  2589. }
  2590. function checkHostPrefix($s)
  2591. {
  2592. if (empty($s)) {
  2593. return $s;
  2594. }
  2595. return (substr($s, -1, 1) == '\\') ? $s : $s . '\\';
  2596. }
  2597. function analyzeIP($ip)
  2598. {
  2599. if (strpos($ip, '/') !== false) {
  2600. $explodeIP = explode('/', $ip);
  2601. $prefix = $explodeIP[1];
  2602. $start_ip = $explodeIP[0];
  2603. $ip_count = 1 << (32 - $prefix);
  2604. $start_ip_long = ip2long($start_ip);
  2605. $last_ip_long = ip2long($start_ip) + $ip_count - 1;
  2606. } elseif (substr_count($ip, '.') == 3) {
  2607. $start_ip_long = ip2long($ip);
  2608. $last_ip_long = ip2long($ip);
  2609. }
  2610. return (isset($start_ip_long) && isset($last_ip_long)) ? array('from' => $start_ip_long, 'to' => $last_ip_long) : false;
  2611. }
  2612. function authProxyRangeCheck($from, $to)
  2613. {
  2614. $approved = false;
  2615. $userIP = ip2long($_SERVER['REMOTE_ADDR']);
  2616. $low = $from;
  2617. $high = $to;
  2618. if ($userIP <= $high && $low <= $userIP) {
  2619. $approved = true;
  2620. }
  2621. return $approved;
  2622. }