You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

875 lines
28 KiB

2 years ago
10 months ago
2 years ago
2 years ago
10 years ago
  1. <?php
  2. use Monolog\Formatter\LineFormatter;
  3. use Monolog\Logger;
  4. use Monolog\Handler\SyslogHandler;
  5. use Monolog\Handler\StreamHandler;
  6. use Movim\Image;
  7. use Movim\ImageSize;
  8. /**
  9. * Log an error
  10. */
  11. function logError(string|Stringable $logs)
  12. {
  13. $log = new Logger('movim');
  14. $log->pushHandler(new SyslogHandler('movim'));
  15. $stream = new StreamHandler(config('paths.log') . '/errors.log');
  16. $stream->setFormatter(new LineFormatter(null, null, true, true));
  17. $log->pushHandler($stream);
  18. $log->error($logs);
  19. }
  20. /**
  21. * Log an info
  22. */
  23. function logInfo(string|Stringable $logs)
  24. {
  25. if (config('daemon.verbose')) {
  26. $log = new Logger('movim');
  27. $log->pushHandler(new SyslogHandler('movim'));
  28. $stream = new StreamHandler(config('paths.log') . '/info.log');
  29. $stream->setFormatter(new LineFormatter(null, null, true));
  30. $log->pushHandler($stream);
  31. $log->info($logs);
  32. }
  33. }
  34. /**
  35. * Log a string, only used for debug purposes
  36. */
  37. function logDebug($logs)
  38. {
  39. $log = new Logger('movim');
  40. $log->pushHandler(new StreamHandler(config('paths.log') . '/debug.log'));
  41. if (is_array($logs)) {
  42. $log->debug('', $logs);
  43. } else {
  44. $log->debug($logs);
  45. }
  46. }
  47. /**
  48. * Return a configuration variable
  49. */
  50. function config(string $key, $default = null)
  51. {
  52. $path = explode('.', $key);
  53. $config = require(CONFIG_PATH . $path[0] . '.php');
  54. if (!isset($path[1])) return $config;
  55. if (array_key_exists($path[1], $config) && !empty($config[$path[1]])) {
  56. $casted = null;
  57. switch ($config[$path[1]]) {
  58. case 'true':
  59. $casted = true;
  60. break;
  61. case 'false':
  62. $casted = false;
  63. break;
  64. default:
  65. $casted = $config[$path[1]];
  66. break;
  67. }
  68. return $casted;
  69. }
  70. return $default;
  71. }
  72. /**
  73. * Check if Opcache is enabled
  74. */
  75. function isOpcacheEnabled(): bool
  76. {
  77. return is_array(opcache_get_status());
  78. }
  79. /**
  80. * List compilable Opcache files
  81. */
  82. function listOpcacheCompilableFiles(): array
  83. {
  84. $files = [];
  85. foreach (['vendor', 'app', 'src'] as $dir) {
  86. $directory = new \RecursiveDirectoryIterator(DOCUMENT_ROOT . '/' . $dir);
  87. $iterator = new \RecursiveIteratorIterator($directory);
  88. $regex = new \RegexIterator($iterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
  89. foreach ($regex as $key => $file) {
  90. array_push($files, $file[0]);
  91. }
  92. }
  93. return $files;
  94. }
  95. /**
  96. * Compile main files in Opcache
  97. */
  98. function compileOpcache()
  99. {
  100. error_reporting(0);
  101. foreach (listOpcacheCompilableFiles() as $file) {
  102. if (opcache_is_script_cached($file)) {
  103. yield @opcache_invalidate($file, true);
  104. } else {
  105. yield @opcache_compile_file($file);
  106. }
  107. }
  108. error_reporting(1);
  109. }
  110. /**
  111. * Check if the session exists
  112. */
  113. function isLogged()
  114. {
  115. return (bool)(\Movim\Session::instance())->get('jid');
  116. }
  117. /**
  118. * Return the list of client types
  119. */
  120. function getClientTypes()
  121. {
  122. return [
  123. 'bot' => __('client.bot'),
  124. 'console' => __('client.console'),
  125. 'pc' => __('client.desktop'),
  126. 'phone' => __('client.phone'),
  127. 'gateway' => __('client.gateway'),
  128. 'handheld' => __('client.phone'),
  129. 'web' => __('client.web'),
  130. 'registered' => __('client.registered')
  131. ];
  132. }
  133. /**
  134. * Resolve infos from a Posts collection
  135. */
  136. function resolveInfos($postCollection)
  137. {
  138. $serverNodes = $postCollection->map(function ($item) {
  139. return ['server' => $item->server, 'node' => $item->node];
  140. })->unique(fn ($item) => $item['server'] . $item['node']);
  141. if ($serverNodes->isNotEmpty()) {
  142. $first = $serverNodes->first();
  143. $infos = \App\Info::where([
  144. 'server' => $first['server'],
  145. 'node' => $first['node'],
  146. ]);
  147. $serverNodes->skip(1)->each(function ($serverNode) use ($infos) {
  148. $infos->orWhere([
  149. 'server' => $serverNode['server'],
  150. 'node' => $serverNode['node'],
  151. ]);
  152. });
  153. $infos = $infos->get()->keyBy(fn ($item) => $item['server'] . $item['node']);
  154. $postCollection->map(function ($item) use ($infos) {
  155. $item->info = $infos->get($item->server . $item->node);
  156. return $item;
  157. });
  158. return $postCollection;
  159. }
  160. }
  161. /**
  162. * Form to array
  163. */
  164. function formToArray(stdClass $form): array
  165. {
  166. $values = [];
  167. foreach ($form as $key => $value) {
  168. $values[$key] = $value->value;
  169. }
  170. return $values;
  171. }
  172. /**
  173. * Return the picture or fallback to the placeholder
  174. */
  175. function getPicture(?string $key, string $placeholder, ImageSize $size = ImageSize::M): string
  176. {
  177. [$width, $height] = match ($size) {
  178. ImageSize::XXL => [1280, 300],
  179. ImageSize::XL => [512, false],
  180. ImageSize::L => [210, false],
  181. ImageSize::M => [120, false],
  182. ImageSize::S => [50, false],
  183. ImageSize::O => [false, false],
  184. };
  185. return (!empty($key) && $url = Image::getOrCreate($key, $width, $height))
  186. ? $url
  187. : avatarPlaceholder($placeholder);
  188. }
  189. /**
  190. * Return a XEP to namespace association
  191. */
  192. function getXepNamespace()
  193. {
  194. return [
  195. '0004' => ['name' => 'Data Forms', 'category' => 'client', 'ns' => 'jabber:x:data'],
  196. '0012' => ['name' => 'Last Activity', 'category' => 'chat', 'ns' => 'jabber:iq:last'],
  197. '0030' => ['name' => 'Service Discovery', 'category' => 'client', 'ns' => 'http://jabber.org/protocol/disco#info'],
  198. '0045' => ['name' => 'Multi-User Chat', 'category' => 'chat', 'ns' => 'http://jabber.org/protocol/muc'],
  199. '0050' => ['name' => 'Ad-Hoc Commands', 'category' => 'client', 'ns' => 'http://jabber.org/protocol/commands'],
  200. '0054' => ['name' => 'vcard-temp', 'category' => 'client', 'ns' => 'vcard-temp'],
  201. '0071' => ['name' => 'XHTML-IM', 'category' => 'chat', 'ns' => 'http://jabber.org/protocol/xhtml-im'],
  202. '0080' => ['name' => 'User Location', 'category' => 'profile', 'ns' => 'http://jabber.org/protocol/geoloc'],
  203. '0084' => ['name' => 'User Avatar', 'category' => 'profile', 'ns' => 'urn:xmpp:avatar:data'],
  204. '0085' => ['name' => 'Chat State Notifications', 'category' => 'chat', 'ns' => 'http://jabber.org/protocol/chatstates'],
  205. '0092' => ['name' => 'Software Version', 'category' => 'client', 'ns' => 'jabber:iq:version'],
  206. '0107' => ['name' => 'User Mood', 'category' => 'profile', 'ns' => 'http://jabber.org/protocol/mood'],
  207. '0108' => ['name' => 'User Activity', 'category' => 'profile', 'ns' => 'http://jabber.org/protocol/activity'],
  208. '0115' => ['name' => 'Entity Capabilities', 'category' => 'client', 'ns' => 'http://jabber.org/protocol/caps'],
  209. '0118' => ['name' => 'User Tune', 'category' => 'profile', 'ns' => 'http://jabber.org/protocol/tune'],
  210. '0124' => ['name' => 'Bidirectional-streams Over Synchronous HTTP (BOSH]', 'category' => 'client', 'ns' => 'http://jabber.org/protocol/httpbind'],
  211. '0152' => ['name' => 'Reachability Addresses', 'category' => 'client', 'ns' => 'urn:xmpp:reach:0'],
  212. '0166' => ['name' => 'Jingle', 'category' => 'jingle', 'ns' => 'urn:xmpp:jingle:1'],
  213. '0167' => ['name' => 'Jingle RTP Sessions', 'category' => 'jingle', 'ns' => 'urn:xmpp:jingle:apps:rtp:1'],
  214. '0172' => ['name' => 'User Nickname', 'category' => 'profile', 'ns' => 'http://jabber.org/protocol/nick'],
  215. '0176' => ['name' => 'Jingle ICE-UDP Transport Method', 'category' => 'jingle', 'ns' => 'urn:xmpp:jingle:transports:ice-udp:1'],
  216. '0177' => ['name' => 'Jingle Raw UDP Transport Method', 'category' => 'jingle', 'ns' => 'urn:xmpp:jingle:transports:raw-udp:1'],
  217. '0184' => ['name' => 'Message Delivery Receipts', 'category' => 'chat', 'ns' => 'urn:xmpp:receipts'],
  218. '0186' => ['name' => 'Invisible Command', 'category' => 'chat', 'ns' => 'urn:xmpp:invisible:0'],
  219. '0199' => ['name' => 'XMPP Ping', 'category' => 'client', 'ns' => 'urn:xmpp:ping'],
  220. '0202' => ['name' => 'Entity Time', 'category' => 'client', 'ns' => 'urn:xmpp:time'],
  221. '0224' => ['name' => 'Attention', 'category' => 'chat', 'ns' => 'urn:xmpp:attention:0'],
  222. '0231' => ['name' => 'Bits of Binary', 'category' => 'chat', 'ns' => 'urn:xmpp:bob'],
  223. '0234' => ['name' => 'Jingle File Transfer', 'category' => 'jingle', 'ns' => 'urn:xmpp:jingle:apps:file-transfer:4'],
  224. '0249' => ['name' => 'Direct MUC Invitations', 'category' => 'chat', 'ns' => 'jabber:x:conference'],
  225. '0277' => ['name' => 'Microblogging over XMPP', 'category' => 'social', 'ns' => 'urn:xmpp:microblog:0'],
  226. '0280' => ['name' => 'Message Carbons', 'category' => 'chat', 'ns' => 'urn:xmpp:carbons:2'],
  227. '0292' => ['name' => 'vCard4 Over XMPP', 'category' => 'profile', 'ns' => 'urn:xmpp:vcard4'],
  228. '0301' => ['name' => 'In-Band Real Time Text', 'category' => 'chat', 'ns' => 'urn:xmpp:rtt:0'],
  229. '0308' => ['name' => 'Last Message Correction', 'category' => 'chat', 'ns' => 'urn:xmpp:message-correct:0'],
  230. '0320' => ['name' => 'Use of DTLS-SRTP in Jingle Sessions', 'category' => 'jingle', 'ns' => 'urn:xmpp:jingle:apps:dtls:0'],
  231. '0327' => ['name' => 'Rayo', 'category' => 'rayo', 'ns' => 'urn:xmpp:rayo:0'],
  232. '0330' => ['name' => 'Pubsub Subscription', 'category' => 'social', 'ns' => 'urn:xmpp:pubsub:subscription'],
  233. '0332' => ['name' => 'HTTP over XMPP transport', 'category' => 'client', 'ns' => 'urn:xmpp:http'],
  234. '0333' => ['name' => 'Chat Markers', 'category' => 'chat', 'ns' => 'urn:xmpp:chat-markers:0'],
  235. '0337' => ['name' => 'Event Logging over XMPP', 'category' => 'client', 'ns' => 'urn:xmpp:eventlog'],
  236. '0338' => ['name' => 'Jingle Grouping Framework', 'category' => 'jingle', 'ns' => 'urn:ietf:rfc:5888'],
  237. '0339' => ['name' => 'Source-Specific Media Attributes in Jingle', 'category' => 'jingle', 'ns' => 'urn:ietf:rfc:5576'],
  238. '0340' => ['name' => 'COnferences with LIghtweight BRIdging (COLIBRI]', 'category' => 'jingle', 'ns' => 'http://jitsi.org/protocol/colibri'],
  239. '0341' => ['name' => 'Rayo CPA', 'category' => 'rayo', 'ns' => 'urn:xmpp:rayo:cpa:0'],
  240. '0342' => ['name' => 'Rayo Fax', 'category' => 'rayo', 'ns' => 'urn:xmpp:rayo:fax:1'],
  241. '0348' => ['name' => 'Signing Forms', 'category' => 'client', 'ns' => 'urn:xmpp:xdata:signature:oauth1'],
  242. '0390' => ['name' => 'Entity Capabilities 2.0', 'category' => 'client', 'ns' => 'urn:xmpp:caps:optimize'],
  243. '0391' => ['name' => 'Jingle Encrypted Transports', 'category' => 'jingle', 'ns' => 'urn:xmpp:jingle:jet:0'],
  244. ];
  245. }
  246. /**
  247. * Return a list of all the country
  248. */
  249. function getCountries()
  250. {
  251. return [
  252. 'AF' => 'Afghanistan',
  253. 'AX' => 'Aland Islands',
  254. 'AL' => 'Albania',
  255. 'DZ' => 'Algeria',
  256. 'AS' => 'American Samoa',
  257. 'AD' => 'Andorra',
  258. 'AO' => 'Angola',
  259. 'AI' => 'Anguilla',
  260. 'AQ' => 'Antarctica',
  261. 'AG' => 'Antigua and Barbuda',
  262. 'AR' => 'Argentina',
  263. 'AM' => 'Armenia',
  264. 'AW' => 'Aruba',
  265. 'AU' => 'Australia',
  266. 'AT' => 'Austria',
  267. 'AZ' => 'Azerbaijan',
  268. 'BS' => 'Bahamas The',
  269. 'BH' => 'Bahrain',
  270. 'BD' => 'Bangladesh',
  271. 'BB' => 'Barbados',
  272. 'BY' => 'Belarus',
  273. 'BE' => 'Belgium',
  274. 'BZ' => 'Belize',
  275. 'BJ' => 'Benin',
  276. 'BM' => 'Bermuda',
  277. 'BT' => 'Bhutan',
  278. 'BO' => 'Bolivia',
  279. 'BA' => 'Bosnia and Herzegovina',
  280. 'BW' => 'Botswana',
  281. 'BV' => 'Bouvet Island (Bouvetoya)',
  282. 'BR' => 'Brazil',
  283. 'IO' => 'British Indian Ocean Territory (Chagos Archipelago)',
  284. 'VG' => 'British Virgin Islands',
  285. 'BN' => 'Brunei Darussalam',
  286. 'BG' => 'Bulgaria',
  287. 'BF' => 'Burkina Faso',
  288. 'BI' => 'Burundi',
  289. 'KH' => 'Cambodia',
  290. 'CM' => 'Cameroon',
  291. 'CA' => 'Canada',
  292. 'CV' => 'Cape Verde',
  293. 'KY' => 'Cayman Islands',
  294. 'CF' => 'Central African Republic',
  295. 'TD' => 'Chad',
  296. 'CL' => 'Chile',
  297. 'CN' => 'China',
  298. 'CX' => 'Christmas Island',
  299. 'CC' => 'Cocos (Keeling) Islands',
  300. 'CO' => 'Colombia',
  301. 'KM' => 'Comoros The',
  302. 'CD' => 'Congo',
  303. 'CG' => 'Congo The',
  304. 'CK' => 'Cook Islands',
  305. 'CR' => 'Costa Rica',
  306. 'CI' => 'Cote d\'Ivoire',
  307. 'HR' => 'Croatia',
  308. 'CU' => 'Cuba',
  309. 'CY' => 'Cyprus',
  310. 'CZ' => 'Czech Republic',
  311. 'DK' => 'Denmark',
  312. 'DJ' => 'Djibouti',
  313. 'DM' => 'Dominica',
  314. 'DO' => 'Dominican Republic',
  315. 'EC' => 'Ecuador',
  316. 'EG' => 'Egypt',
  317. 'SV' => 'El Salvador',
  318. 'GQ' => 'Equatorial Guinea',
  319. 'ER' => 'Eritrea',
  320. 'EE' => 'Estonia',
  321. 'ET' => 'Ethiopia',
  322. 'FO' => 'Faroe Islands',
  323. 'FK' => 'Falkland Islands (Malvinas)',
  324. 'FJ' => 'Fiji The Fiji Islands',
  325. 'FI' => 'Finland',
  326. 'FR' => 'France, French Republic',
  327. 'GF' => 'French Guiana',
  328. 'PF' => 'French Polynesia',
  329. 'TF' => 'French Southern Territories',
  330. 'GA' => 'Gabon',
  331. 'GM' => 'Gambia The',
  332. 'GE' => 'Georgia',
  333. 'DE' => 'Germany',
  334. 'GH' => 'Ghana',
  335. 'GI' => 'Gibraltar',
  336. 'GR' => 'Greece',
  337. 'GL' => 'Greenland',
  338. 'GD' => 'Grenada',
  339. 'GP' => 'Guadeloupe',
  340. 'GU' => 'Guam',
  341. 'GT' => 'Guatemala',
  342. 'GG' => 'Guernsey',
  343. 'GN' => 'Guinea',
  344. 'GW' => 'Guinea-Bissau',
  345. 'GY' => 'Guyana',
  346. 'HT' => 'Haiti',
  347. 'HM' => 'Heard Island and McDonald Islands',
  348. 'VA' => 'Holy See (Vatican City State)',
  349. 'HN' => 'Honduras',
  350. 'HK' => 'Hong Kong',
  351. 'HU' => 'Hungary',
  352. 'IS' => 'Iceland',
  353. 'IN' => 'India',
  354. 'ID' => 'Indonesia',
  355. 'IR' => 'Iran',
  356. 'IQ' => 'Iraq',
  357. 'IE' => 'Ireland',
  358. 'IM' => 'Isle of Man',
  359. 'IL' => 'Israel',
  360. 'IT' => 'Italy',
  361. 'JM' => 'Jamaica',
  362. 'JP' => 'Japan',
  363. 'JE' => 'Jersey',
  364. 'JO' => 'Jordan',
  365. 'KZ' => 'Kazakhstan',
  366. 'KE' => 'Kenya',
  367. 'KI' => 'Kiribati',
  368. 'KP' => 'Korea',
  369. 'KR' => 'Korea',
  370. 'KW' => 'Kuwait',
  371. 'KG' => 'Kyrgyz Republic',
  372. 'LA' => 'Lao',
  373. 'LV' => 'Latvia',
  374. 'LB' => 'Lebanon',
  375. 'LS' => 'Lesotho',
  376. 'LR' => 'Liberia',
  377. 'LY' => 'Libyan Arab Jamahiriya',
  378. 'LI' => 'Liechtenstein',
  379. 'LT' => 'Lithuania',
  380. 'LU' => 'Luxembourg',
  381. 'MO' => 'Macao',
  382. 'MK' => 'Macedonia',
  383. 'MG' => 'Madagascar',
  384. 'MW' => 'Malawi',
  385. 'MY' => 'Malaysia',
  386. 'MV' => 'Maldives',
  387. 'ML' => 'Mali',
  388. 'MT' => 'Malta',
  389. 'MH' => 'Marshall Islands',
  390. 'MQ' => 'Martinique',
  391. 'MR' => 'Mauritania',
  392. 'MU' => 'Mauritius',
  393. 'YT' => 'Mayotte',
  394. 'MX' => 'Mexico',
  395. 'FM' => 'Micronesia',
  396. 'MD' => 'Moldova',
  397. 'MC' => 'Monaco',
  398. 'MN' => 'Mongolia',
  399. 'ME' => 'Montenegro',
  400. 'MS' => 'Montserrat',
  401. 'MA' => 'Morocco',
  402. 'MZ' => 'Mozambique',
  403. 'MM' => 'Myanmar',
  404. 'NA' => 'Namibia',
  405. 'NR' => 'Nauru',
  406. 'NP' => 'Nepal',
  407. 'AN' => 'Netherlands Antilles',
  408. 'NL' => 'Netherlands The',
  409. 'NC' => 'New Caledonia',
  410. 'NZ' => 'New Zealand',
  411. 'NI' => 'Nicaragua',
  412. 'NE' => 'Niger',
  413. 'NG' => 'Nigeria',
  414. 'NU' => 'Niue',
  415. 'NF' => 'Norfolk Island',
  416. 'MP' => 'Northern Mariana Islands',
  417. 'NO' => 'Norway',
  418. 'OM' => 'Oman',
  419. 'PK' => 'Pakistan',
  420. 'PW' => 'Palau',
  421. 'PS' => 'Palestinian Territory',
  422. 'PA' => 'Panama',
  423. 'PG' => 'Papua New Guinea',
  424. 'PY' => 'Paraguay',
  425. 'PE' => 'Peru',
  426. 'PH' => 'Philippines',
  427. 'PN' => 'Pitcairn Islands',
  428. 'PL' => 'Poland',
  429. 'PT' => 'Portugal, Portuguese Republic',
  430. 'PR' => 'Puerto Rico',
  431. 'QA' => 'Qatar',
  432. 'RE' => 'Reunion',
  433. 'RO' => 'Romania',
  434. 'RU' => 'Russian Federation',
  435. 'RW' => 'Rwanda',
  436. 'BL' => 'Saint Barthelemy',
  437. 'SH' => 'Saint Helena',
  438. 'KN' => 'Saint Kitts and Nevis',
  439. 'LC' => 'Saint Lucia',
  440. 'MF' => 'Saint Martin',
  441. 'PM' => 'Saint Pierre and Miquelon',
  442. 'VC' => 'Saint Vincent and The Grenadines',
  443. 'WS' => 'Samoa',
  444. 'SM' => 'San Marino',
  445. 'ST' => 'Sao Tome and Principe',
  446. 'SA' => 'Saudi Arabia',
  447. 'SN' => 'Senegal',
  448. 'RS' => 'Serbia',
  449. 'SC' => 'Seychelles',
  450. 'SL' => 'Sierra Leone',
  451. 'SG' => 'Singapore',
  452. 'SK' => 'Slovakia (Slovak Republic)',
  453. 'SI' => 'Slovenia',
  454. 'SB' => 'Solomon Islands',
  455. 'SO' => 'Somalia, Somali Republic',
  456. 'ZA' => 'South Africa',
  457. 'GS' => 'South Georgia and The South Sandwich Islands',
  458. 'ES' => 'Spain',
  459. 'LK' => 'Sri Lanka',
  460. 'SD' => 'Sudan',
  461. 'SR' => 'Suriname',
  462. 'SJ' => 'Svalbard & Jan Mayen Islands',
  463. 'SZ' => 'Swaziland',
  464. 'SE' => 'Sweden',
  465. 'CH' => 'Switzerland, Swiss Confederation',
  466. 'SY' => 'Syrian Arab Republic',
  467. 'TW' => 'Taiwan',
  468. 'TJ' => 'Tajikistan',
  469. 'TZ' => 'Tanzania',
  470. 'TH' => 'Thailand',
  471. 'TL' => 'Timor-Leste',
  472. 'TG' => 'Togo',
  473. 'TK' => 'Tokelau',
  474. 'TO' => 'Tonga',
  475. 'TT' => 'Trinidad and Tobago',
  476. 'TN' => 'Tunisia',
  477. 'TR' => 'Turkey',
  478. 'TM' => 'Turkmenistan',
  479. 'TC' => 'Turks and Caicos Islands',
  480. 'TV' => 'Tuvalu',
  481. 'UG' => 'Uganda',
  482. 'UA' => 'Ukraine',
  483. 'AE' => 'United Arab Emirates',
  484. 'GB' => 'United Kingdom',
  485. 'US' => 'United States of America',
  486. 'UM' => 'United States Minor Outlying Islands',
  487. 'VI' => 'United States Virgin Islands',
  488. 'UY' => 'Uruguay, Eastern Republic of',
  489. 'UZ' => 'Uzbekistan',
  490. 'VU' => 'Vanuatu',
  491. 'VE' => 'Venezuela',
  492. 'VN' => 'Vietnam',
  493. 'WF' => 'Wallis and Futuna',
  494. 'EH' => 'Western Sahara',
  495. 'YE' => 'Yemen',
  496. 'ZM' => 'Zambia',
  497. 'ZW' => 'Zimbabwe'
  498. ];
  499. }
  500. function getImgurThumbnail(string $uri)
  501. {
  502. $matches = [];
  503. preg_match('/https?:\/\/i.imgur.com\/([a-zA-Z0-9]{7})(.*)/', $uri, $matches);
  504. if (!empty($matches)) {
  505. return 'https://i.imgur.com/' . $matches[1] . 'g' . $matches[2];
  506. }
  507. }
  508. function getPresences()
  509. {
  510. return [
  511. 1 => __('presence.online'),
  512. 2 => __('presence.away'),
  513. 3 => __('presence.dnd'),
  514. 4 => __('presence.xa'),
  515. 5 => __('presence.offline'),
  516. 6 => __('presence.error')
  517. ];
  518. }
  519. function getPresencesTxt()
  520. {
  521. return [
  522. 1 => 'online',
  523. 2 => 'away',
  524. 3 => 'dnd',
  525. 4 => 'xa',
  526. 5 => 'offline',
  527. 6 => 'server_error'
  528. ];
  529. }
  530. /**
  531. * Map the XMPP form vars to Material Symbols
  532. */
  533. function varToIcons(string $var)
  534. {
  535. $icons = [
  536. // Pubsub
  537. 'pubsub#deliver_payloads' => 'add_box',
  538. 'pubsub#deliver_notifications' => 'notifications',
  539. 'pubsub#notify_config' => 'notifications',
  540. 'pubsub#notify_delete' => 'delete',
  541. 'pubsub#notify_retract' => 'delete_sweep',
  542. 'pubsub#persist_items' => 'save',
  543. 'pubsub#presence_based_delivery' => 'notifications_active',
  544. 'pubsub#purge_offline' => 'delete_forever',
  545. 'pubsub#subscribe' => 'how_to_reg',
  546. 'pubsub#type' => 'space_dashboard',
  547. // Muc
  548. 'muc#roomconfig_persistentroom' => 'save',
  549. 'muc#roomconfig_publicroom' => 'wifi_tethering',
  550. 'muc#roomconfig_passwordprotectedroom' => 'lock',
  551. 'muc#roomconfig_membersonly' => 'playlist_add_check',
  552. 'muc#roomconfig_moderatedroom' => 'stars',
  553. 'muc#roomconfig_changesubject' => 'title',
  554. 'muc#roomconfig_allowinvites' => 'mail',
  555. 'allow_private_messages' => 'message',
  556. 'allow_query_users' => 'portrait',
  557. 'allow_visitor_nickchange' => '3p',
  558. 'allow_visitor_status' => 'description',
  559. 'allow_voice_requests' => 'voice_selection',
  560. 'allow_subscription' => 'how_to_reg',
  561. 'enable_hats' => 'badge',
  562. 'mam' => 'archive',
  563. 'members_by_default' => 'remember_me',
  564. 'public_list' => 'public',
  565. ];
  566. if (array_key_exists($var, $icons)) {
  567. return $icons[$var];
  568. }
  569. return 'tune';
  570. }
  571. /**
  572. * Generate a standard UUID
  573. */
  574. function generateUUID($string = false)
  575. {
  576. $data = ($string != false) ? $string : openssl_random_pseudo_bytes(16);
  577. $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0010
  578. $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
  579. return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
  580. }
  581. /**
  582. * @desc Generate a simple random key
  583. * @params The size of the key
  584. */
  585. function generateKey(?int $size = 16): string
  586. {
  587. $hashChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  588. $hash = '';
  589. for ($i = 0; $i < $size; $i++) {
  590. $hash .= $hashChars[random_int(0, strlen($hashChars) - 1)];
  591. }
  592. return $hash;
  593. }
  594. define('DEFAULT_HTTP_USER_AGENT', 'Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0');
  595. /**
  596. * @desc Request a url async
  597. */
  598. function requestAsyncURL(string $url, int $timeout = 10, array $headers = [])
  599. {
  600. $browser = new React\Http\Browser;
  601. return $browser->withTimeout($timeout)->get($url, $headers);
  602. }
  603. /*
  604. * @desc Request a simple url
  605. */
  606. function requestURL(string $url, int $timeout = 10, $post = false, bool $json = false, array $headers = [])
  607. {
  608. if ($json) {
  609. array_push($headers, 'Accept: application/json');
  610. }
  611. $ch = curl_init($url);
  612. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  613. //curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
  614. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  615. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  616. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  617. curl_setopt($ch, CURLOPT_USERAGENT, DEFAULT_HTTP_USER_AGENT);
  618. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  619. if ($post) {
  620. curl_setopt($ch, CURLOPT_POST, 1);
  621. curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  622. }
  623. // Disable SSL if the host requested is the local one
  624. if (parse_url(config('daemon.url'), PHP_URL_HOST) == parse_url($url, PHP_URL_HOST)) {
  625. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  626. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  627. }
  628. $content = curl_exec($ch);
  629. return curl_errno($ch) == 0 ? $content : false;
  630. }
  631. /*
  632. * Request the headers of a URL
  633. */
  634. function requestHeaders(string $url, $timeout = 2)
  635. {
  636. $ch = curl_init($url);
  637. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  638. //curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
  639. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  640. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  641. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  642. curl_setopt($ch, CURLOPT_HEADER, 1);
  643. curl_setopt($ch, CURLOPT_NOBODY, 1);
  644. curl_setopt($ch, CURLOPT_USERAGENT, DEFAULT_HTTP_USER_AGENT);
  645. curl_exec($ch);
  646. return curl_getinfo($ch);
  647. }
  648. /**
  649. * Request the internal API
  650. */
  651. function requestAPI(string $action, int $timeout = 2, $post = false)
  652. {
  653. $ch = curl_init('http:/' . $action);
  654. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  655. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  656. curl_setopt($ch, CURLOPT_UNIX_SOCKET_PATH, API_SOCKET);
  657. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  658. if (is_array($post)) {
  659. curl_setopt($ch, CURLOPT_POST, 1);
  660. curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  661. }
  662. $content = curl_exec($ch);
  663. return curl_errno($ch) == 0 ? $content : false;
  664. }
  665. /**
  666. * @desc Get distance between two coordinates
  667. *
  668. * @param float $latitudeFrom
  669. * @param float $longitudeFrom
  670. * @param float $latitudeTo
  671. * @param float $longitudeTo
  672. *
  673. * @return float [km]
  674. */
  675. function getDistance(float $latitudeFrom, float $longitudeFrom, float $latitudeTo, float $longitudeTo): float
  676. {
  677. $rad = M_PI / 180;
  678. $theta = $longitudeFrom - $longitudeTo;
  679. $dist = sin($latitudeFrom * $rad)
  680. * sin($latitudeTo * $rad) + cos($latitudeFrom * $rad)
  681. * cos($latitudeTo * $rad) * cos($theta * $rad);
  682. return acos($dist) / $rad * 60 * 1.853;
  683. }
  684. /*
  685. * @desc Get the URI of a smiley
  686. */
  687. function getSmileyPath($id)
  688. {
  689. return BASE_URI . 'theme/img/emojis/svg/' . $id . '.svg';
  690. }
  691. /**
  692. * @desc Return the url of an avatar placeholder
  693. */
  694. function avatarPlaceholder(string $id): string
  695. {
  696. return \Movim\Route::urlize('picture', false, ['type' => 'avatar', 'id' => urlencode($id)]);
  697. }
  698. /*
  699. * @desc Protect a picture URL by using the internal Proxy
  700. */
  701. function protectPicture($url)
  702. {
  703. $emptyPicture = \Movim\Route::urlize('picture', '');
  704. $emptyPicture = preg_replace("(^//)", 'https://', $emptyPicture);
  705. // The picture is already protected
  706. if (substr($url, 0, strlen($emptyPicture)) === $emptyPicture) {
  707. return $url;
  708. }
  709. return \Movim\Route::urlize('picture', false, ['type' => 'picture', 'url' => urlencode($url)]);
  710. }
  711. /*
  712. * @desc Translate something
  713. */
  714. function __()
  715. {
  716. $args = func_get_args();
  717. $l = Movim\i18n\Locale::start();
  718. $string = array_shift($args);
  719. return $l->translate($string, $args);
  720. }
  721. /*
  722. * @desc Get the browser name from a user agent
  723. */
  724. function getBrowser(string $userAgent): ?string
  725. {
  726. $t = strtolower($userAgent);
  727. $t = ' ' . $t;
  728. if (strpos($t, 'opera')) return 'Opera';
  729. elseif (strpos($t, 'edge')) return 'Edge';
  730. elseif (strpos($t, 'chrome')) return 'Chrome';
  731. elseif (strpos($t, 'safari')) return 'Safari';
  732. elseif (strpos($t, 'firefox')) return 'Firefox';
  733. }
  734. /*
  735. * @desc Get the platform from the user agent
  736. */
  737. function getPlatform(string $userAgent): ?string
  738. {
  739. $oses = [
  740. '/windows nt 10/i' => 'Windows 10',
  741. '/windows nt 6.3/i' => 'Windows 8.1',
  742. '/windows nt 6.2/i' => 'Windows 8',
  743. '/windows nt 6.1/i' => 'Windows 7',
  744. '/windows nt 6.0/i' => 'Windows Vista',
  745. '/macintosh|mac os x/i' => 'Mac OS X',
  746. '/mac_powerpc/i' => 'Mac OS 9',
  747. '/linux/i' => 'Linux',
  748. '/ubuntu/i' => 'Ubuntu',
  749. '/iphone/i' => 'iPhone',
  750. '/ipod/i' => 'iPod',
  751. '/ipad/i' => 'iPad',
  752. '/android/i' => 'Android',
  753. '/blackberry/i' => 'BlackBerry',
  754. '/webos/i' => 'Mobile'
  755. ];
  756. foreach ($oses as $regex => $value) {
  757. if (preg_match($regex, $userAgent)) {
  758. return $value;
  759. }
  760. }
  761. }
  762. /**
  763. * @desc Get PHP hash to IANA hashes conversion
  764. * https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xhtml
  765. * https://www.php.net/manual/en/function.hash-algos.php
  766. */
  767. function phpToIANAHash(): array
  768. {
  769. return [
  770. 'md2' => 'md2',
  771. 'md5' => 'md5',
  772. 'sha1' => 'sha1',
  773. 'sha224' => 'sha-224',
  774. 'sha256' => 'sha-256',
  775. 'sha384' => 'sha-384',
  776. 'sha512' => 'sha-512',
  777. ];
  778. }
  779. function IANAHashToPhp(): array
  780. {
  781. return [
  782. 'md2' => 'md2',
  783. 'md5' => 'md5',
  784. 'sha1' => 'sha1', // https://xmpp.org/extensions/xep-0231.html#algo
  785. 'sha-224' => 'sha224',
  786. 'sha256' => 'sha256', // retro-compatibility
  787. 'sha-256' => 'sha256',
  788. 'sha-384' => 'sha384',
  789. 'sha-512' => 'sha512',
  790. ];
  791. }