model.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  1. <?php
  2. /**
  3. * Model file of im module.
  4. *
  5. * @copyright Copyright 2009-2023 ZenTao Software (Qingdao) Co., Ltd. (www.zentao.net)
  6. * @author Xiying Guan <guanxiying@cnezsoft.com>
  7. * @package im
  8. * @license ZOSL (http://zpl.pub/page/zoslv1.html)
  9. * @version $Id$
  10. * @Link http://xuan.im
  11. */
  12. class imModel extends model
  13. {
  14. /**
  15. * Sub-model list, these models are stored at im/model/.
  16. *
  17. * @access public
  18. */
  19. public $models = array('chat', 'message', 'user', 'conference', 'bot');
  20. /**
  21. * @var imChat
  22. */
  23. public $chat;
  24. /**
  25. * @var imMessage
  26. */
  27. public $message;
  28. /**
  29. * @var imUser
  30. */
  31. public $user;
  32. /**
  33. * @var imConference
  34. */
  35. public $conference;
  36. /**
  37. * @var imBot
  38. */
  39. public $bot;
  40. /**
  41. * __construct loads and inits sub-models.
  42. *
  43. * @access public
  44. * @return void
  45. */
  46. public function __construct()
  47. {
  48. parent::__construct();dao::$autoExclude = false;
  49. if((isset($_SERVER['RR_RELAY']) || isset($_SERVER['RR_MODE'])) && !commonModel::isLicensedMethod('im', 'roadrunner')) die;
  50. $modelPath = dirname(__FILE__) . DS . "model" . DS;
  51. foreach($this->models as $model)
  52. {
  53. helper::import($modelPath . "$model.php");
  54. $className = "im$model";
  55. $this->$model = new $className($this->appName, $this);
  56. }
  57. }
  58. /**
  59. * Get apiScheme info.
  60. *
  61. * @param string $key
  62. * @access public
  63. * @return mixed
  64. */
  65. public function getApiScheme($key = '')
  66. {
  67. $schemeFile = $this->app->getExtensionRoot() . 'xuan/im/apischeme.json';
  68. $scheme = json_decode(trim(file_get_contents($schemeFile)), true);
  69. if(!$key) return $scheme;
  70. return zget($scheme, $key, '');
  71. }
  72. /**
  73. * Send formatted output.
  74. *
  75. * @param array|object $output Example: array('result' => 'success', 'data' => $messages, 'users' => $userIdList, ...);
  76. * @param string $schemeName
  77. * @access public
  78. * @return void
  79. */
  80. public function sendOutput($output, $schemeName = 'RAW')
  81. {
  82. $output = $this->formatOutput($output, $schemeName);
  83. $output = trim($output);
  84. return $this->app->output($this->app->encrypt($output));
  85. }
  86. /**
  87. * Send formatted output group.
  88. *
  89. * @param array|object $outputs Example: array('result' => 'success', 'data' => $messages, 'users' => $userIdList, ...);
  90. * @access public
  91. * @return void
  92. */
  93. public function sendOutputGroup($outputs)
  94. {
  95. $formats = '';
  96. foreach($outputs as $output)
  97. {
  98. if(is_array($output))
  99. {
  100. $output = (object)$output;
  101. }
  102. if(!isset($output->method))
  103. {
  104. $stack = debug_backtrace(false, 2);
  105. $output->method = isset($stack[1]) ? $stack[1]['function'] : '';
  106. }
  107. $formatedOutput = $this->formatOutput($output, strtolower($output->method) . 'Response');
  108. $formats .= $formatedOutput . "\n";
  109. }
  110. $formats = trim($formats);
  111. return $this->app->output($this->app->encrypt($formats));
  112. }
  113. /**
  114. * Format output data.
  115. *
  116. * @param array|object $data Example: array('result' => 'success', 'data' => $messages, 'users' => $userIdList, ...);
  117. * @param string $map
  118. * @param bool $returnRaw
  119. * @param bool $prependName
  120. * @access public
  121. * @return object|string
  122. */
  123. public function formatOutput($data, $map = 'RAW', $returnRaw = false, $prependName = true)
  124. {
  125. if(!empty($this->app->debug)) $this->app->log("format output($map): " . json_encode($data));
  126. $output = new stdclass();
  127. foreach($data as $key => $value)
  128. {
  129. $output->$key = $value;
  130. }
  131. $output->device = zget($this->app->input, 'device', 'desktop');
  132. $output->userID = zget($data, 'userID', '0');
  133. $output->result = zget($data, 'result', 'success');
  134. $output->method = zget($data, 'method', $this->app->getMethodName());
  135. $output->rid = zget($this->app->input, 'rid');
  136. if(!empty($output->users))
  137. {
  138. if(is_numeric($output->users)) $output->users = array((int)$output->users); // Convert single user id to array.
  139. $output->users = array_map('intval', $output->users);
  140. $output->users = array_filter($output->users);
  141. $output->users = array_values($output->users);
  142. }
  143. if(isset($output->userID) && is_string($output->userID))
  144. {
  145. $output->userID = (int)$output->userID;
  146. }
  147. if($map != 'RAW')
  148. {
  149. $maps = zget($this->config->maps, $map, array());
  150. /* Using requestPack as fallback maps */
  151. if(empty($maps)) $maps = zget($this->config->maps, 'responsePack');
  152. $data = self::encodeOutput($output, $maps);
  153. }
  154. else
  155. {
  156. $map = strtolower($output->method) . 'Response';
  157. }
  158. $data = $prependName ? array($map, $data) : $data;
  159. if(!empty($this->app->debug)) $this->app->log("encoded output($map): " . json_encode($data));
  160. if($returnRaw) return $data;
  161. $users = isset($output->users) ? $output->users : array();
  162. $header = $this->appendResponseHeader($output->userID, $users, $output->method, $output->result);
  163. return json_encode($header) . "\n" . json_encode($data);
  164. }
  165. /**
  166. * Encode output.
  167. *
  168. * @param object $output
  169. * @param object|string|boolean $map
  170. * @static
  171. * @access public
  172. * @return array|object
  173. */
  174. public static function encodeOutput($output, $map)
  175. {
  176. if(empty($map)) return $output;
  177. $output = (array) $output;
  178. /* If map is not final map array, decode with map's dataType setting.*/
  179. if(isset($map['name']) and isset($map['dataType'])) $map = $map['dataType'];
  180. if(isset($map['name']) and !isset($map['dataType'])) $map = array($map);
  181. $data = array();
  182. foreach($map as $key => $prop)
  183. {
  184. $indexName = $prop['name'];
  185. if($prop['type'] == 'basic')
  186. {
  187. if(isset($prop['options']))
  188. {
  189. $options = array_flip($prop['options']);
  190. $data[$key] = zget($options, zget($output, $indexName, ''));
  191. }
  192. else
  193. {
  194. $data[$key] = zget($output, $indexName, '');
  195. }
  196. continue;
  197. }
  198. if($prop['type'] == 'object')
  199. {
  200. if(isset($output[$indexName])) $data[$key] = self::encodeOutput($output[$indexName], $prop['dataType']);
  201. continue;
  202. }
  203. if($prop['type'] == 'list')
  204. {
  205. $tmpOutput = array();
  206. if(isset($output[$indexName]))
  207. {
  208. foreach($output[$indexName] as $item)
  209. {
  210. $tmpOutput[] = self::encodeOutput($item, $prop['dataType']);
  211. }
  212. }
  213. $data[$key] = $tmpOutput;
  214. }
  215. }
  216. return $data;
  217. }
  218. /**
  219. * Batch encode output.
  220. *
  221. * @param array $array
  222. * @param bool|object|string $map
  223. * @static
  224. * @access public
  225. * @return array
  226. */
  227. public static function batchEncodeOutput($array, $map)
  228. {
  229. $data = array();
  230. if(empty($array)) return $data;
  231. foreach($array as $output) $data[] = self::encodeOutput($output, $map);
  232. return $data;
  233. }
  234. /**
  235. * Append header for xxd to response.
  236. *
  237. * @param array $output
  238. * @param string $from current user id
  239. * @param string|int|array $to id list of users to notify : 123,2,3,4,76,423
  240. * @param string $method
  241. * @param string $result
  242. * @access public
  243. * @return string
  244. */
  245. public function appendResponseHeader($from, $to = 0, $method = '', $result = 'success')
  246. {
  247. $header = array();
  248. if(empty($to)) $to = array($this->app->input['userID']);
  249. if(!is_array($to)) $to = array($to);
  250. if(!$method) $method = strtolower($this->app->getMethodName());
  251. $device = $this->app->input['device'];
  252. $lang = $this->app->input['lang'];
  253. if(!isset($from) || empty($from)) $from = $this->app->input['userID'];
  254. $header[] = $to;
  255. $header[] = $from;
  256. $header[] = $method;
  257. $header[] = $result;
  258. $header[] = $device;
  259. $header[] = $lang;
  260. return $header;
  261. }
  262. /**
  263. * Get output data of user list.
  264. *
  265. * @param array $identities
  266. * @param int $userID
  267. * @param bool $returnRaw
  268. * @access public
  269. * @return object
  270. */
  271. public function getUserListOutput($identities, $userID, $returnRaw = false)
  272. {
  273. $output = new stdclass();
  274. $users = $this->userGetList($status = '', $identities, $idAsKey = false);
  275. if(dao::isError())
  276. {
  277. $output->result = 'fail';
  278. $output->message = 'Get userlist failed.';
  279. return $this->formatOutput($output, 'messageResponsePack', $returnRaw);
  280. }
  281. else
  282. {
  283. $output->result = 'success';
  284. $output->users = $userID;
  285. $output->data = $users;
  286. $output->method = 'usergetlist';
  287. if(empty($identities))
  288. {
  289. $this->app->loadLang('user');
  290. $roles = $this->lang->user->roleList;
  291. $allDepts = $this->loadModel('dept')->getListByType('dept');
  292. $depts = array();
  293. foreach($allDepts as $id => $dept)
  294. {
  295. $depts[$id] = array('name' => $dept->name, 'order' => (int)$dept->order, 'parent' => (int)$dept->parent);
  296. }
  297. $output->roles = $roles;
  298. $output->depts = $depts;
  299. }
  300. else
  301. {
  302. $output->partial = $identities;
  303. }
  304. }
  305. return $this->formatOutput($output, 'usergetlistResponse', $returnRaw);
  306. }
  307. /**
  308. * Create gid.
  309. * @access public
  310. * @return string
  311. */
  312. public static function createGID()
  313. {
  314. $id = md5(microtime() . mt_rand());
  315. return substr($id, 0, 8) . '-' . substr($id, 8, 4) . '-' . substr($id, 12, 4) . '-' . substr($id, 16, 4) . '-' . substr($id, 20, 12);
  316. }
  317. /**
  318. * Download xxd.
  319. *
  320. * @param object $setting
  321. * @param string $downloadType
  322. * @access public
  323. * @return array
  324. */
  325. public function downloadXXD($setting, $downloadType)
  326. {
  327. set_time_limit(0);
  328. $system = $this->getSystem($setting->os);
  329. $version = $this->config->xuanxuan->version;
  330. $xxdDirectory = $this->app->tmpRoot . 'xxd' . DS . $version;
  331. $basePackage = $xxdDirectory . DS . $system . '.base.zip';
  332. $xxdFileName = "xxd.$version.$system.zip";
  333. $downloadCDNLink = $this->config->im->xxdDownloadUrl . $version . '/' . $xxdFileName;
  334. if(!is_dir($xxdDirectory)) mkdir($xxdDirectory, 0777, true);
  335. if(!file_exists($basePackage) && $downloadType == 'package')
  336. {
  337. $ch = curl_init();
  338. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  339. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  340. curl_setopt($ch, CURLOPT_URL, $downloadCDNLink);
  341. $data = curl_exec($ch);
  342. curl_close($ch);
  343. if(empty($data)) return array('result' => 'fail', 'message' => 'Failed to download xxd package.');
  344. $fopenPackage = fopen($basePackage, 'w');
  345. fwrite($fopenPackage, $data);
  346. }
  347. $data = new stdClass();
  348. $data->xxdDirectory = $xxdDirectory;
  349. $data->sslcrt = $setting->sslcrt;
  350. $data->sslkey = $setting->sslkey;
  351. $data->basePackage = $basePackage;
  352. $data->xxdFileName = $xxdFileName;
  353. $data->host = trim($this->getServer(), '/') . (zget($this->config->xuanxuan, 'backend', 'xxb') == 'ranzhi' ? dirname($this->config->webRoot) : $this->config->webRoot);
  354. $data->ip = $setting->ip ?: '0.0.0.0';
  355. $data->commonPort = $setting->commonPort ?: '11443';
  356. $data->chatPort = $setting->chatPort ?: '11444';
  357. $data->https = $setting->https ?: 'on';
  358. $data->enableAES = $setting->aes == 'off' ? 0 : 1;
  359. $data->uploadPath = 'files/';
  360. $data->uploadFileSize = $setting->uploadFileSize ?: '20';
  361. $data->pollingInterval = isset($this->config->xuanxuan->pollingInterval) ? $this->config->xuanxuan->pollingInterval : 15;
  362. $data->maxOnlineUser = isset($setting->maxOnlineUser) ? $setting->maxOnlineUser : 0;
  363. $data->logPath = 'log/';
  364. $data->certPath = 'cert/';
  365. $data->debug = 0;
  366. $data->key = $this->config->xuanxuan->key;
  367. $data->syncConfig = 1;
  368. $data->thumbnail = 1;
  369. if($downloadType == 'config')
  370. {
  371. $configContent = $this->createXxdConfigFile($data);
  372. if(!empty($configContent)) $this->loadModel('file')->sendDownHeader('xxd.conf', 'conf', $configContent['zh']);
  373. }
  374. elseif($downloadType == 'package')
  375. {
  376. $packageFileName = $this->createXxdPackage($data);
  377. if(!empty($packageFileName)) return array('result' => 'success', 'message' => helper::createLink('im', 'downloadXxdPackage', "xxdFileName=$xxdFileName"));
  378. }
  379. return array('result' => 'fail', 'message' => 'error');
  380. }
  381. /**
  382. * create xxd config file
  383. *
  384. * @param object $setting
  385. * @access public
  386. * @return array
  387. */
  388. public function createXxdConfigFile($setting)
  389. {
  390. $configParamsList = $this->config->im->xxdConfig;
  391. // Replace template variable.
  392. $lineMaxLength = 0;
  393. foreach($configParamsList as $configParams)
  394. {
  395. if($configParams == 'host' || $configParams == 'key')
  396. {
  397. $config[$configParams] = $setting->$configParams;
  398. }
  399. elseif(strpos($configParams, '=') !== false)
  400. {
  401. $configItem = explode('=', $configParams);
  402. $config[$configItem[0]] = $configItem[0] . '=' . $configItem[1];
  403. }
  404. else
  405. {
  406. $config[$configParams] = $configParams . '=' . $setting->$configParams;
  407. if($configParams == 'uploadFileSize') $config[$configParams] .= 'M';
  408. }
  409. $lineMaxLength = strlen($configParams) > $lineMaxLength ? strlen($configParams) : $lineMaxLength;
  410. }
  411. $lineMaxLength += 10;
  412. // Add parameter notes
  413. $contentZH = '[server]' . "\n";
  414. $contentEN = '[server]' . "\n";
  415. foreach($config as $type => $configValue)
  416. {
  417. if($type == 'host' || $type == 'key') continue;
  418. $configValue = str_replace(PHP_EOL, '', $configValue);
  419. $configlength = strlen($configValue);
  420. for($i = 0; $i < ($lineMaxLength - $configlength); $i++) $configValue .= ' ';
  421. $contentZH .= $configValue . $this->lang->im->xxdConfigNote['zh'][$type] . "\n";
  422. $contentEN .= $configValue . $this->lang->im->xxdConfigNote['en'][$type] . "\n";
  423. }
  424. // Add backend
  425. $backend = "\n" . '[backend]' . "\n";
  426. $backendFoot = 'default=' . $config['host'] . 'x.php,' . $config['key'];
  427. $backendFoot = str_replace(PHP_EOL, '', $backendFoot) . "\n";
  428. $backendZH = $backend . $this->lang->im->xxdConfigNote['zh']['backend'] . "\n" . $backendFoot;
  429. $backendEN = $backend . $this->lang->im->xxdConfigNote['en']['backend'] . "\n" . $backendFoot;
  430. $contentZH .= $backendZH;
  431. $contentEN .= $backendEN;
  432. return array('zh' => $contentZH, 'en' => $contentEN);
  433. }
  434. /**
  435. * create xxd package
  436. *
  437. * @param object
  438. * @access public
  439. * @return string
  440. */
  441. public function createXxdPackage($setting)
  442. {
  443. $configContent = $this->createXxdConfigFile($setting);
  444. if(empty($configContent)) return false;
  445. // unzip package
  446. $this->app->loadClass('pclzip', true);
  447. $basePackage = new pclzip($setting->basePackage);
  448. $result = $basePackage->extract(
  449. PCLZIP_OPT_PATH, $setting->xxdDirectory
  450. );
  451. if($result == 0) $basePackage->errorInfo(true);
  452. // Replace config file.
  453. $baseFilePath = $result[0]['filename'];
  454. $packageName = $result[0]['stored_filename'];
  455. unlink($baseFilePath . 'config/xxd.conf');
  456. unlink($baseFilePath . 'config/xxd.en.conf');
  457. file_put_contents($baseFilePath . 'config/xxd.conf', $configContent['zh']);
  458. file_put_contents($baseFilePath . 'config/xxd.en.conf', $configContent['en']);
  459. // https add certificate
  460. if(isset($setting->https) && $setting->https == 'on')
  461. {
  462. if(!is_dir($baseFilePath . 'cert')) mkdir($baseFilePath . 'cert', 0777);
  463. file_put_contents($baseFilePath . 'cert/xxd.crt', $setting->sslcrt);
  464. file_put_contents($baseFilePath . 'cert/xxd.key', $setting->sslkey);
  465. }
  466. // zip xxd file
  467. chdir($setting->xxdDirectory);
  468. $xxdZipName = $setting->xxdDirectory . "/" . $setting->xxdFileName;
  469. $xxdZip = new pclzip($xxdZipName);
  470. $xxdResult = $xxdZip->create($packageName, PCLZIP_OPT_TEMP_FILE_ON);
  471. if($xxdResult == 0) return false;
  472. return $xxdResult[0]['filename'];
  473. }
  474. /**
  475. * revise operating system name.
  476. *
  477. * @param string $os name
  478. * @access public
  479. * @return string
  480. */
  481. public function getSystem($os)
  482. {
  483. return zget($this->config->im->osMap, $os, 'win64');
  484. }
  485. /**
  486. * Get server, if server is localhost, try get from stored login url.
  487. *
  488. * @access public
  489. * @return string
  490. */
  491. public function getServer()
  492. {
  493. $server = commonModel::getSysURL();
  494. if(!empty($this->config->xuanxuan->server)) $server = $this->config->xuanxuan->server;
  495. $serverURLComponents = parse_url($server);
  496. if(!empty($serverURLComponents['host']) && in_array($serverURLComponents['host'], array('127.0.0.1', 'localhost', '::1')))
  497. {
  498. $loginURL = $this->loadModel('setting')->getItem("owner=system&module=im&section=loginurl&key={$this->app->session->userID}");
  499. if(!empty($loginURL))
  500. {
  501. $loginURLComponents = parse_url($loginURL);
  502. if(!empty($loginURLComponents['host'])) $server = substr_replace($server, $loginURLComponents['host'], strpos($server, $serverURLComponents['host']), strlen($serverURLComponents['host']));
  503. }
  504. }
  505. return $server;
  506. }
  507. /**
  508. * UploadFile a file.
  509. *
  510. * @param string $fileName
  511. * @param string $path
  512. * @param int $size
  513. * @param int $time
  514. * @param int $userID
  515. * @param string $users
  516. * @param object $chat
  517. * @access public
  518. * @return int
  519. */
  520. public function uploadFile($fileName, $path, $size, $time, $userID, $users, $chat)
  521. {
  522. $user = $this->userGetByID($userID);
  523. $extension = $this->loadModel('file')->getExtension($fileName); // if file has no extension or is "danger",return "txt, but $fileName is the origin file name"
  524. $file = new stdclass();
  525. $file->pathname = $path;
  526. $file->title = preg_replace("/\.$extension$/", '', $fileName);
  527. $file->extension = $extension;
  528. $file->size = $size;
  529. $file->objectType = 'chat';
  530. $file->objectID = $chat->id;
  531. $file->createdBy = !empty($user->account) ? $user->account : '';
  532. $file->createdDate = date(DT_DATETIME1, $time);
  533. $this->dao->insert(TABLE_FILE)->data($file)->exec();
  534. $fileID = $this->dao->lastInsertID();
  535. $path .= md5($fileName . $fileID . $time);
  536. $this->dao->update(TABLE_FILE)->set('pathname')->eq($path)->where('id')->eq($fileID)->exec();
  537. return $fileID;
  538. }
  539. /**
  540. * Save xxd start time.
  541. *
  542. * @access public
  543. * @return bool
  544. */
  545. public function setXxdStartTime()
  546. {
  547. $this->loadModel('setting')->setItem('system.common.xxd.start', helper::now());
  548. return !dao::isError();
  549. }
  550. /**
  551. * update last poll.
  552. *
  553. * @access public
  554. * @return void
  555. */
  556. public function updateLastPoll()
  557. {
  558. $this->loadModel('setting')->setItem('system.common.xxd.lastPoll', helper::now());
  559. }
  560. /**
  561. * check xxb config.
  562. *
  563. * @access public
  564. * @return bool
  565. */
  566. public function checkXXBConfig()
  567. {
  568. $xxbConfig = $this->config->xuanxuan;
  569. $notEmptyFields = array('key', 'server', 'ip', 'chatPort', 'commonPort');
  570. foreach($notEmptyFields as $field) if(empty($xxbConfig->$field)) return false;
  571. if($xxbConfig->https == 'on' && (empty($xxbConfig->sslcrt) || empty($xxbConfig->sslkey))) return false;
  572. return true;
  573. }
  574. /**
  575. * Get xxd run time.
  576. *
  577. * @param int $timestamp
  578. * @param int $count
  579. * @access public
  580. * @return string
  581. */
  582. public function getXxdRunTime($timestamp, $count = 0)
  583. {
  584. if($count > 1) return '';
  585. if($timestamp > 86400)
  586. {
  587. return floor($timestamp / 86400) . $this->lang->im->day . $this->getXxdRunTime($timestamp%86400, ++$count);
  588. }
  589. else if($timestamp > 3600)
  590. {
  591. return floor($timestamp / 3600) . $this->lang->im->hours . $this->getXxdRunTime($timestamp%3600, ++$count);
  592. }
  593. else if($timestamp > 60)
  594. {
  595. return floor($timestamp / 60) . $this->lang->im->minute . $this->getXxdRunTime($timestamp%60, ++$count);
  596. }
  597. else
  598. {
  599. return $timestamp . $this->lang->im->secs;
  600. }
  601. }
  602. /**
  603. * Get xxd status.
  604. *
  605. * @access public
  606. * @return string
  607. */
  608. public function getXxdStatus()
  609. {
  610. $this->app->loadLang('client');
  611. $now = helper::now();
  612. $xxdStatus = 'offline';
  613. $polling = empty($this->config->xuanxuan->pollingInterval) ? 60 : $this->config->xuanxuan->pollingInterval;
  614. $lastPoll = $this->loadModel('setting')->getItem("owner=system&module=common&section=xxd&key=lastPoll");
  615. $xxdStartDate = zget($this->config->xxd, 'start', $this->lang->client->noData);
  616. if((strtotime($now) - strtotime($xxdStartDate) < $polling) || (strtotime($now) - strtotime($lastPoll)) < (3 + $polling))
  617. {
  618. $xxdStatus = 'online';
  619. }
  620. else if((strtotime($now) - strtotime($lastPoll)) > (3 + $polling))
  621. {
  622. $xxdStatus = 'offline';
  623. }
  624. return $xxdStatus;
  625. }
  626. /**
  627. * Get signed time.
  628. * Other program can extend this function.
  629. *
  630. * @param string $account
  631. * @access public
  632. * @return string | int
  633. */
  634. public function getSignedTime($account = '')
  635. {
  636. return 0;
  637. }
  638. /**
  639. * Get extension list.
  640. * @param $userID
  641. * @return array
  642. */
  643. public function getExtensionList($userID)
  644. {
  645. $entries = array();
  646. $allEntries = array();
  647. $time = time();
  648. $baseURL = commonModel::getSysURL();
  649. $entryList = $this->dao->select('*')->from(TABLE_ENTRY)->orderBy('`order`, id')->fetchAll();
  650. $files = $this->dao->select('id, pathname, objectID')->from(TABLE_FILE)->where('objectType')->eq('entry')->fetchAll('objectID');
  651. foreach($entryList as $entry)
  652. {
  653. $data = new stdclass();
  654. $data->id = $entry->id;
  655. $data->url = strpos($entry->login, 'http') !== 0 ? str_replace('../', $baseURL . $this->config->webRoot, $entry->login) : $entry->login;
  656. $allEntries[] = $data;
  657. }
  658. $_SERVER['SCRIPT_NAME'] = str_replace('x.php', 'index.php', $_SERVER['SCRIPT_NAME']);
  659. foreach($entryList as $entry)
  660. {
  661. if($entry->status != 'online') continue;
  662. if(strpos(',' . $entry->platform . ',', ',xuanxuan,') === false) continue;
  663. $token = '';
  664. if(isset($files[$entry->id]->pathname))
  665. {
  666. $token = '&time=' . $time . '&token=' . md5($files[$entry->id]->pathname . $time);
  667. }
  668. $data = new stdClass();
  669. $data->entryID = (int)$entry->id;
  670. $data->name = $entry->code;
  671. $data->displayName = $entry->name;
  672. $data->abbrName = $entry->abbr;
  673. $data->optional = $entry->optional;
  674. $data->enable = $entry->enable;
  675. $data->webViewUrl = strpos($entry->login, 'http') !== 0 ? str_replace('../', $baseURL . $this->config->webRoot, $entry->login) : $entry->login;
  676. $data->download = empty($entry->package) ? '' : $baseURL . helper::createLink('file', 'download', "fileID={$entry->package}&mouse=" . $token);
  677. $data->md5 = empty($entry->package) ? '' : md5($entry->package);
  678. $data->logo = empty($entry->logo) ? '' : $baseURL . $this->config->webRoot . ltrim($entry->logo, '/');
  679. if($entry->sso) $data->data = $allEntries;
  680. $entries[] = $data;
  681. }
  682. return $entries;
  683. }
  684. /**
  685. * transfer ip to number
  686. *
  687. * @param string $ip
  688. * @return int
  689. */
  690. public function getIPLong($ip) {
  691. return bindec(decbin(ip2long($ip)));
  692. }
  693. /**
  694. * Check whether IP is valid
  695. *
  696. * @param string $ip
  697. * @return bool
  698. */
  699. public function checkIPValidity($ip) {
  700. if(filter_var($ip, FILTER_VALIDATE_IP)) return true;
  701. return false;
  702. }
  703. /**
  704. * Check whether CIDR is valid
  705. *
  706. * @param string $ip
  707. * @return bool
  708. */
  709. public function checkCIDRValidity($cidr)
  710. {
  711. $parts = explode('/', $cidr);
  712. if(count($parts) != 2) return false;
  713. $ip = $parts[0];
  714. if(!$this->checkIPValidity($ip)) return false;
  715. $netmask = $parts[1];
  716. if(!is_numeric($netmask)) return false;
  717. $netmask = intval($parts[1]);
  718. if($netmask < 0 || $netmask > 32) return false;
  719. return true;
  720. }
  721. /**
  722. * check whether IP in CIDR
  723. * @param string|number $ip
  724. * @param string $cidr
  725. * @return bool
  726. */
  727. public function checkIPInCIDR($ip, $cidr)
  728. {
  729. $cidr = explode('/', $cidr);
  730. if(is_string($ip)) $ip = $this->getIPLong($ip);
  731. $startIp = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
  732. $endIp = long2ip((ip2long($startIp)) + pow(2, (32 - (int)$cidr[1])) - 1);
  733. $startIp = $this->getIPLong($startIp);
  734. $endIp = $this->getIPLong($endIp);
  735. return $ip >= $startIp && $ip <= $endIp;
  736. }
  737. /**
  738. * check whether IP in CIDRs
  739. * @param string $ip
  740. * @param string $startIp
  741. * @param string $endIp
  742. * @return bool
  743. */
  744. public function checkIPInCIDRs($ip, $cidrs)
  745. {
  746. $originCidrs = $cidrs;
  747. $cidrs = explode(',', $cidrs);
  748. if(count($cidrs) == 0)
  749. {
  750. if($this->checkCIDRValidity($originCidrs))
  751. {
  752. $cidrs = array($originCidrs);
  753. }
  754. else
  755. {
  756. return false;
  757. }
  758. }
  759. $ip = $this->getIPLong($ip);
  760. foreach($cidrs as $cidr) if($this->checkIPInCIDR($ip, $cidr)) return true;
  761. return false;
  762. }
  763. /**
  764. * __call functions defined in model.
  765. *
  766. * @param string $function
  767. * @param array $arguments
  768. * @access public
  769. * @return void
  770. */
  771. public function __call($function, $arguments)
  772. {
  773. foreach($this->models as $model)
  774. {
  775. if(strpos(strtolower($function), $model) === 0)
  776. {
  777. $trimedFunction = substr($function, strlen($model));
  778. if(is_callable(array($this->$model, $trimedFunction))) return call_user_func_array(array($this->$model, $trimedFunction), $arguments);
  779. }
  780. }
  781. $this->app->triggerError("Method im::$function not exists.", __FILE__, __LINE__, $exit = true);
  782. }
  783. }