├── servicemanager
├── servicemanager.exe
├── servicemanager_mac
├── servicemanager_nix_32
├── servicemanager_nix_64
├── servicemanager_nix.systemd
├── servicemanager_mac.launchd
└── servicemanager_nix.sysvinit
├── .gitignore
├── support
├── phpseclib
│ ├── openssl.cnf
│ ├── license.txt
│ └── Crypt
│ │ ├── AES.php
│ │ └── RC4.php
├── event_manager.php
├── crc32_stream.php
├── str_basics.php
├── servicemanager.php
├── remotedapi_web_server.php
├── multi_async_helper.php
├── webroute.php
├── deflate_stream.php
├── random.php
├── cli.php
└── utf_utils.php
├── sdks
└── php
│ ├── sdk_remotedapi.php
│ ├── crc32_stream.php
│ ├── sdk_cloud_storage_server_files.php
│ ├── webroute.php
│ ├── deflate_stream.php
│ ├── sdk_cloud_storage_server_api_base.php
│ ├── random.php
│ └── utf_utils.php
├── manage.php
└── README.md
/servicemanager/servicemanager.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cubiclesoft/cloud-storage-server/master/servicemanager/servicemanager.exe
--------------------------------------------------------------------------------
/servicemanager/servicemanager_mac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cubiclesoft/cloud-storage-server/master/servicemanager/servicemanager_mac
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /data/*
2 | /test.php
3 | /user_init/*
4 | /server_exts/scripts.php
5 | /server_exts/feeds.php
6 | /support/createprocess.exe
7 |
--------------------------------------------------------------------------------
/servicemanager/servicemanager_nix_32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cubiclesoft/cloud-storage-server/master/servicemanager/servicemanager_nix_32
--------------------------------------------------------------------------------
/servicemanager/servicemanager_nix_64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cubiclesoft/cloud-storage-server/master/servicemanager/servicemanager_nix_64
--------------------------------------------------------------------------------
/support/phpseclib/openssl.cnf:
--------------------------------------------------------------------------------
1 | # minimalist openssl.cnf file for use with phpseclib
2 |
3 | HOME = .
4 | RANDFILE = $ENV::HOME/.rnd
5 |
6 | [ v3_ca ]
7 |
--------------------------------------------------------------------------------
/servicemanager/servicemanager_nix.systemd:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=@SERVICENAME@
3 | After=syslog.target network.target remote-fs.target nss-lookup.target
4 |
5 | [Service]
6 | Type=forking
7 | PIDFile=@SERVICEPIDFILE@
8 | ExecStart='@SERVICEMANAGER@' start '@SERVICENAME@'
9 | ExecReload='@SERVICEMANAGER@' reload '@SERVICENAME@'
10 | ExecStop='@SERVICEMANAGER@' stop '@SERVICENAME@'
11 | Restart=no
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/servicemanager/servicemanager_mac.launchd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | com.servicemanager.@SERVICENAME@
7 | ProgramArguments
8 |
9 | @SERVICEMANAGER@
10 | run
11 | @SERVICENAME@
12 |
13 | KeepAlive
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/servicemanager/servicemanager_nix.sysvinit:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | ### BEGIN INIT INFO
3 | # Provides: @SERVICENAME@
4 | # Required-Start: $local_fs $remote_fs $network
5 | # Required-Stop: $local_fs $remote_fs $network
6 | # Default-Start: 2 3 4 5
7 | # Default-Stop: 0 1 6
8 | # Short-Description: starts @SERVICENAME@
9 | # Description: starts @SERVICENAME@
10 | ### END INIT INFO
11 |
12 | '@SERVICEMANAGER@' $1 '@SERVICENAME@'
13 |
14 | if [ "$?" != 0 ] ; then
15 | echo
16 | echo "Service Manager returned a failure code."
17 | echo
18 | echo "Usage: $0 {start|stop|restart|reload|status|configfile|custom-action-name|uninstall}"
19 |
20 | exit 1
21 | fi
22 |
--------------------------------------------------------------------------------
/support/phpseclib/license.txt:
--------------------------------------------------------------------------------
1 | Copyright 2007-2016 TerraFrost and other contributors
2 | http://phpseclib.sourceforge.net/
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/support/event_manager.php:
--------------------------------------------------------------------------------
1 | events = array();
12 | $this->nextid = 1;
13 | }
14 |
15 | public function Register($eventname, $objorfuncname, $funcname = false)
16 | {
17 | if ($objorfuncname === false)
18 | {
19 | $objorfuncname = $funcname;
20 | $funcname = false;
21 | }
22 |
23 | if (!isset($this->events[$eventname])) $this->events[$eventname] = array("used" => 0, "callbacks" => array());
24 | $this->events[$eventname]["callbacks"][$this->nextid] = ($funcname === false ? $objorfuncname : array($objorfuncname, $funcname));
25 |
26 | $id = $this->nextid;
27 | $this->nextid++;
28 |
29 | return $id;
30 | }
31 |
32 | public function Unregister($eventname, $id)
33 | {
34 | if (isset($this->events[$eventname])) unset($this->events[$eventname]["callbacks"][$id]);
35 | }
36 |
37 | public function GetAllUsedCounts()
38 | {
39 | $result = array();
40 | foreach ($this->events as $eventname => $info) $result[$eventname] = $info["used"];
41 |
42 | return $result;
43 | }
44 |
45 | public function GetUsedCount($eventname)
46 | {
47 | return (isset($this->events[$eventname]) ? $this->events[$eventname]["used"] : 0);
48 | }
49 |
50 | public function Fire($eventname, $options)
51 | {
52 | $results = array();
53 | if ($eventname !== "" && isset($this->events[$eventname]))
54 | {
55 | foreach ($this->events[$eventname]["callbacks"] as $id => $func)
56 | {
57 | if (!is_callable($func)) unset($this->events[$eventname]["callbacks"][$id]);
58 | else
59 | {
60 | $result = call_user_func_array($func, $options);
61 | if (isset($result)) $results[] = $result;
62 | }
63 | }
64 |
65 | if (count($this->events[$eventname]["callbacks"])) $this->events[$eventname]["used"]++;
66 | else unset($this->events[$eventname]);
67 | }
68 |
69 | if (isset($this->events[""]))
70 | {
71 | foreach ($this->events[""]["callbacks"] as $id => $func)
72 | {
73 | if (!is_callable($func)) unset($this->events[""]["callbacks"][$id]);
74 | else
75 | {
76 | $result = call_user_func_array($func, array_merge(array($eventname), $options));
77 | if (isset($result)) $results[] = $result;
78 | }
79 | }
80 |
81 | if (count($this->events[""]["callbacks"])) $this->events[""]["used"]++;
82 | else unset($this->events[""]);
83 | }
84 |
85 | return $results;
86 | }
87 | }
88 | ?>
--------------------------------------------------------------------------------
/sdks/php/sdk_remotedapi.php:
--------------------------------------------------------------------------------
1 | false, "error" => WebRoute::WRTranslate("Invalid Remoted API URL scheme."), "errorcode" => "invalid_scheme");
45 |
46 | $result["url"] = $url;
47 |
48 | return $result;
49 | }
50 |
51 | if ($url2["loginusername"] === "") return array("success" => false, "error" => WebRoute::WRTranslate("Remoted API URL is missing client key."), "errorcode" => "missing_client_key");
52 |
53 | $options["headers"]["X-Remoted-APIKey"] = $url2["loginusername"];
54 |
55 | $url2["scheme"] = ($url2["scheme"] === "rwr" ? "wr" : "wrs");
56 | unset($url2["loginusername"]);
57 | unset($url2["login"]);
58 |
59 | $url = HTTP::CondenseURL($url2);
60 |
61 | $result = $wr->Connect($url, false, $timeout, $options, $web);
62 | if (!$result["success"]) return $result;
63 |
64 | $options["fp"] = $result["fp"];
65 | }
66 |
67 | return $result;
68 | }
69 | }
70 | ?>
--------------------------------------------------------------------------------
/sdks/php/crc32_stream.php:
--------------------------------------------------------------------------------
1 | 0x04C11DB7, "start" => 0xFFFFFFFF, "xor" => 0xFFFFFFFF, "refdata" => 1, "refcrc" => 1);
14 |
15 | public function __construct()
16 | {
17 | $this->open = false;
18 | }
19 |
20 | public function Init($options = false)
21 | {
22 | if ($options === false && function_exists("hash_init")) $this->hash = hash_init("crc32b");
23 | else
24 | {
25 | if ($options === false) $options = self::$default;
26 |
27 | $this->hash = false;
28 | $this->crctable = array();
29 | $poly = $this->LIM32($options["poly"]);
30 | for ($x = 0; $x < 256; $x++)
31 | {
32 | $c = $this->SHL32($x, 24);
33 | for ($y = 0; $y < 8; $y++) $c = $this->SHL32($c, 1) ^ ($c & 0x80000000 ? $poly : 0);
34 | $this->crctable[$x] = $c;
35 | }
36 |
37 | $this->datareflect = $options["refdata"];
38 | $this->crcreflect = $options["refcrc"];
39 | $this->firstcrc = $options["start"];
40 | $this->currcrc = $options["start"];
41 | $this->finalxor = $options["xor"];
42 | }
43 |
44 | $this->open = true;
45 | }
46 |
47 | public function AddData($data)
48 | {
49 | if (!$this->open) return false;
50 |
51 | if ($this->hash !== false) hash_update($this->hash, $data);
52 | else
53 | {
54 | $y = strlen($data);
55 |
56 | for ($x = 0; $x < $y; $x++)
57 | {
58 | if ($this->datareflect) $this->currcrc = $this->SHL32($this->currcrc, 8) ^ $this->crctable[$this->SHR32($this->currcrc, 24) ^ self::$revlookup[ord($data[$x])]];
59 | else $this->currcrc = $this->SHL32($this->currcrc, 8) ^ $this->crctable[$this->SHR32($this->currcrc, 24) ^ ord($data[$x])];
60 | }
61 | }
62 |
63 | return true;
64 | }
65 |
66 | public function Finalize()
67 | {
68 | if (!$this->open) return false;
69 |
70 | if ($this->hash !== false)
71 | {
72 | $result = hexdec(hash_final($this->hash));
73 |
74 | $this->hash = hash_init("crc32b");
75 | }
76 | else
77 | {
78 | if ($this->crcreflect)
79 | {
80 | $tempcrc = $this->currcrc;
81 | $this->currcrc = self::$revlookup[$this->SHR32($tempcrc, 24)] | $this->SHL32(self::$revlookup[$this->SHR32($tempcrc, 16) & 0xFF], 8) | $this->SHL32(self::$revlookup[$this->SHR32($tempcrc, 8) & 0xFF], 16) | $this->SHL32(self::$revlookup[$this->LIM32($tempcrc & 0xFF)], 24);
82 | }
83 | $result = $this->currcrc ^ $this->finalxor;
84 |
85 | $this->currcrc = $this->firstcrc;
86 | }
87 |
88 | return $result;
89 | }
90 |
91 | // These functions are a hacky, but effective way of enforcing unsigned 32-bit integers onto a generic signed int.
92 | // Allow bitwise operations to work across platforms. Minimum integer size must be 32-bit.
93 | private function SHR32($num, $bits)
94 | {
95 | $num = (int)$num;
96 | if ($bits < 0) $bits = 0;
97 |
98 | if ($num < 0 && $bits)
99 | {
100 | $num = ($num >> 1) & 0x7FFFFFFF;
101 | $bits--;
102 | }
103 |
104 | return $this->LIM32($num >> $bits);
105 | }
106 |
107 | private function SHL32($num, $bits)
108 | {
109 | if ($bits < 0) $bits = 0;
110 |
111 | return $this->LIM32((int)$num << $bits);
112 | }
113 |
114 | private function LIM32($num)
115 | {
116 | return (int)((int)$num & 0xFFFFFFFF);
117 | }
118 | }
119 | ?>
--------------------------------------------------------------------------------
/support/crc32_stream.php:
--------------------------------------------------------------------------------
1 | 0x04C11DB7, "start" => 0xFFFFFFFF, "xor" => 0xFFFFFFFF, "refdata" => 1, "refcrc" => 1);
14 |
15 | public function __construct()
16 | {
17 | $this->open = false;
18 | }
19 |
20 | public function Init($options = false)
21 | {
22 | if ($options === false && function_exists("hash_init")) $this->hash = hash_init("crc32b");
23 | else
24 | {
25 | if ($options === false) $options = self::$default;
26 |
27 | $this->hash = false;
28 | $this->crctable = array();
29 | $poly = $this->LIM32($options["poly"]);
30 | for ($x = 0; $x < 256; $x++)
31 | {
32 | $c = $this->SHL32($x, 24);
33 | for ($y = 0; $y < 8; $y++) $c = $this->SHL32($c, 1) ^ ($c & 0x80000000 ? $poly : 0);
34 | $this->crctable[$x] = $c;
35 | }
36 |
37 | $this->datareflect = $options["refdata"];
38 | $this->crcreflect = $options["refcrc"];
39 | $this->firstcrc = $options["start"];
40 | $this->currcrc = $options["start"];
41 | $this->finalxor = $options["xor"];
42 | }
43 |
44 | $this->open = true;
45 | }
46 |
47 | public function AddData($data)
48 | {
49 | if (!$this->open) return false;
50 |
51 | if ($this->hash !== false) hash_update($this->hash, $data);
52 | else
53 | {
54 | $y = strlen($data);
55 |
56 | for ($x = 0; $x < $y; $x++)
57 | {
58 | if ($this->datareflect) $this->currcrc = $this->SHL32($this->currcrc, 8) ^ $this->crctable[$this->SHR32($this->currcrc, 24) ^ self::$revlookup[ord($data[$x])]];
59 | else $this->currcrc = $this->SHL32($this->currcrc, 8) ^ $this->crctable[$this->SHR32($this->currcrc, 24) ^ ord($data[$x])];
60 | }
61 | }
62 |
63 | return true;
64 | }
65 |
66 | public function Finalize()
67 | {
68 | if (!$this->open) return false;
69 |
70 | if ($this->hash !== false)
71 | {
72 | $result = hexdec(hash_final($this->hash));
73 |
74 | $this->hash = hash_init("crc32b");
75 | }
76 | else
77 | {
78 | if ($this->crcreflect)
79 | {
80 | $tempcrc = $this->currcrc;
81 | $this->currcrc = self::$revlookup[$this->SHR32($tempcrc, 24)] | $this->SHL32(self::$revlookup[$this->SHR32($tempcrc, 16) & 0xFF], 8) | $this->SHL32(self::$revlookup[$this->SHR32($tempcrc, 8) & 0xFF], 16) | $this->SHL32(self::$revlookup[$this->LIM32($tempcrc & 0xFF)], 24);
82 | }
83 | $result = $this->currcrc ^ $this->finalxor;
84 |
85 | $this->currcrc = $this->firstcrc;
86 | }
87 |
88 | return $result;
89 | }
90 |
91 | // These functions are a hacky, but effective way of enforcing unsigned 32-bit integers onto a generic signed int.
92 | // Allow bitwise operations to work across platforms. Minimum integer size must be 32-bit.
93 | private function SHR32($num, $bits)
94 | {
95 | $num = (int)$num;
96 | if ($bits < 0) $bits = 0;
97 |
98 | if ($num < 0 && $bits)
99 | {
100 | $num = ($num >> 1) & 0x7FFFFFFF;
101 | $bits--;
102 | }
103 |
104 | return $this->LIM32($num >> $bits);
105 | }
106 |
107 | private function SHL32($num, $bits)
108 | {
109 | if ($bits < 0) $bits = 0;
110 |
111 | return $this->LIM32((int)$num << $bits);
112 | }
113 |
114 | private function LIM32($num)
115 | {
116 | return (int)((int)$num & 0xFFFFFFFF);
117 | }
118 | }
119 | ?>
--------------------------------------------------------------------------------
/support/str_basics.php:
--------------------------------------------------------------------------------
1 | $val)
10 | {
11 | if (is_string($val)) $_REQUEST[$key] = trim($val);
12 | else if (is_array($val))
13 | {
14 | $_REQUEST[$key] = array();
15 | foreach ($val as $key2 => $val2) $_REQUEST[$key][$key2] = (is_string($val2) ? trim($val2) : $val2);
16 | }
17 | else $_REQUEST[$key] = $val;
18 | }
19 | }
20 |
21 | // Cleans up all PHP input issues so that $_REQUEST may be used as expected.
22 | public static function ProcessAllInput()
23 | {
24 | self::ProcessSingleInput($_COOKIE);
25 | self::ProcessSingleInput($_GET);
26 | self::ProcessSingleInput($_POST);
27 | }
28 |
29 | public static function ExtractPathname($dirfile)
30 | {
31 | $dirfile = str_replace("\\", "/", $dirfile);
32 | $pos = strrpos($dirfile, "/");
33 | if ($pos === false) $dirfile = "";
34 | else $dirfile = substr($dirfile, 0, $pos + 1);
35 |
36 | return $dirfile;
37 | }
38 |
39 | public static function ExtractFilename($dirfile)
40 | {
41 | $dirfile = str_replace("\\", "/", $dirfile);
42 | $pos = strrpos($dirfile, "/");
43 | if ($pos !== false) $dirfile = substr($dirfile, $pos + 1);
44 |
45 | return $dirfile;
46 | }
47 |
48 | public static function ExtractFileExtension($dirfile)
49 | {
50 | $dirfile = self::ExtractFilename($dirfile);
51 | $pos = strrpos($dirfile, ".");
52 | if ($pos !== false) $dirfile = substr($dirfile, $pos + 1);
53 | else $dirfile = "";
54 |
55 | return $dirfile;
56 | }
57 |
58 | public static function ExtractFilenameNoExtension($dirfile)
59 | {
60 | $dirfile = self::ExtractFilename($dirfile);
61 | $pos = strrpos($dirfile, ".");
62 | if ($pos !== false) $dirfile = substr($dirfile, 0, $pos);
63 |
64 | return $dirfile;
65 | }
66 |
67 | // Makes an input filename safe for use.
68 | // Allows a very limited number of characters through.
69 | public static function FilenameSafe($filename)
70 | {
71 | return preg_replace('/\s+/', "-", trim(trim(preg_replace('/[^A-Za-z0-9_.\-]/', " ", $filename), ".")));
72 | }
73 |
74 | public static function ReplaceNewlines($replacewith, $data)
75 | {
76 | $data = str_replace("\r\n", "\n", $data);
77 | $data = str_replace("\r", "\n", $data);
78 | $data = str_replace("\n", $replacewith, $data);
79 |
80 | return $data;
81 | }
82 |
83 | public static function LineInput($data, &$pos)
84 | {
85 | $CR = ord("\r");
86 | $LF = ord("\n");
87 |
88 | $result = "";
89 | $y = strlen($data);
90 | if ($pos > $y) $pos = $y;
91 | while ($pos < $y && ord($data[$pos]) != $CR && ord($data[$pos]) != $LF)
92 | {
93 | $result .= $data[$pos];
94 | $pos++;
95 | }
96 | if ($pos + 1 < $y && ord($data[$pos]) == $CR && ord($data[$pos + 1]) == $LF) $pos++;
97 | if ($pos < $y) $pos++;
98 |
99 | return $result;
100 | }
101 |
102 | // Constant-time string comparison. Ported from CubicleSoft C++ code.
103 | public static function CTstrcmp($secret, $userinput)
104 | {
105 | $sx = 0;
106 | $sy = strlen($secret);
107 | $uy = strlen($userinput);
108 | $result = $sy - $uy;
109 | for ($ux = 0; $ux < $uy; $ux++)
110 | {
111 | $result |= ord($userinput[$ux]) ^ ord($secret[$sx]);
112 | $sx = ($sx + 1) % $sy;
113 | }
114 |
115 | return $result;
116 | }
117 |
118 | public static function ConvertUserStrToBytes($str)
119 | {
120 | $str = trim($str);
121 | $num = (double)$str;
122 | if (strtoupper(substr($str, -1)) == "B") $str = substr($str, 0, -1);
123 | switch (strtoupper(substr($str, -1)))
124 | {
125 | case "P": $num *= 1024;
126 | case "T": $num *= 1024;
127 | case "G": $num *= 1024;
128 | case "M": $num *= 1024;
129 | case "K": $num *= 1024;
130 | }
131 |
132 | return $num;
133 | }
134 |
135 | public static function ConvertBytesToUserStr($num)
136 | {
137 | $num = (double)$num;
138 |
139 | if ($num < 0) return "0 B";
140 | if ($num < 1024) return number_format($num, 0) . " B";
141 | if ($num < 1048576) return str_replace(".0 ", "", number_format($num / 1024, 1)) . " KB";
142 | if ($num < 1073741824) return str_replace(".0 ", "", number_format($num / 1048576, 1)) . " MB";
143 | if ($num < 1099511627776.0) return str_replace(".0 ", "", number_format($num / 1073741824.0, 1)) . " GB";
144 | if ($num < 1125899906842624.0) return str_replace(".0 ", "", number_format($num / 1099511627776.0, 1)) . " TB";
145 |
146 | return str_replace(".0 ", "", number_format($num / 1125899906842624.0, 1)) . " PB";
147 | }
148 |
149 | public static function JSSafe($data)
150 | {
151 | return str_replace(array("'", "\r", "\n"), array("\\'", "\\r", "\\n"), $data);
152 | }
153 | }
154 | ?>
--------------------------------------------------------------------------------
/support/servicemanager.php:
--------------------------------------------------------------------------------
1 | rootpath = str_replace(array("\\", "/"), DIRECTORY_SEPARATOR, $rootpath);
12 | }
13 |
14 | public function Install($servicename, $phpfile, $args, $options = array(), $display = false)
15 | {
16 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
17 | if (!isset($options["dir"])) $options["dir"] = $this->rootpath;
18 | foreach ($options as $key => $val) $cmd .= " " . escapeshellarg("-" . $key . "=" . $val);
19 | $cmd .= " install " . escapeshellarg($servicename);
20 | $cmd .= " " . escapeshellarg($phpfile . ".notify");
21 | $cmd .= " " . escapeshellarg($this->GetPHPBinary());
22 | $cmd .= " " . escapeshellarg($phpfile);
23 | foreach ($args as $arg) $cmd .= " " . escapeshellarg($arg);
24 |
25 | return $this->RunCommand($cmd, $display);
26 | }
27 |
28 | public function Uninstall($servicename, $display = false)
29 | {
30 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
31 | $cmd .= " uninstall " . escapeshellarg($servicename);
32 |
33 | return $this->RunCommand($cmd, $display);
34 | }
35 |
36 | public function Start($servicename, $display = false)
37 | {
38 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
39 | $cmd .= " start " . escapeshellarg($servicename);
40 |
41 | return $this->RunCommand($cmd, $display);
42 | }
43 |
44 | public function Stop($servicename, $display = false)
45 | {
46 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
47 | $cmd .= " stop " . escapeshellarg($servicename);
48 |
49 | return $this->RunCommand($cmd, $display);
50 | }
51 |
52 | public function Restart($servicename, $display = false)
53 | {
54 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
55 | $cmd .= " restart " . escapeshellarg($servicename);
56 |
57 | return $this->RunCommand($cmd, $display);
58 | }
59 |
60 | public function Reload($servicename, $display = false)
61 | {
62 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
63 | $cmd .= " reload " . escapeshellarg($servicename);
64 |
65 | return $this->RunCommand($cmd, $display);
66 | }
67 |
68 | public function WaitFor($servicename, $display = false)
69 | {
70 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
71 | $cmd .= " waitfor " . escapeshellarg($servicename);
72 |
73 | return $this->RunCommand($cmd, $display);
74 | }
75 |
76 | public function Status($servicename, $display = false)
77 | {
78 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
79 | $cmd .= " status " . escapeshellarg($servicename);
80 |
81 | return $this->RunCommand($cmd, $display);
82 | }
83 |
84 | public function GetConfig($servicename)
85 | {
86 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
87 | $cmd .= " configfile " . escapeshellarg($servicename);
88 |
89 | $result = $this->RunCommand($cmd);
90 | if (!$result["success"]) return $result;
91 |
92 | $filename = trim($result["output"]);
93 | if (strtolower(substr($filename, 0, 6)) === "error:") return array("success" => false, "error" => self::SMTranslate("Unable to locate the '%s' configuration file.", $servicename), "errorcode" => "missing_config", "info" => $result);
94 |
95 | $fp = fopen($filename, "rb");
96 | if ($fp === false) return array("success" => false, "error" => self::SMTranslate("Unable to open the configuration file '%s' for reading.", $filename), "errorcode" => "fopen_failed");
97 |
98 | $result = array(
99 | "success" => true,
100 | "filename" => $filename,
101 | "options" => array()
102 | );
103 |
104 | while (($line = fgets($fp)) !== false)
105 | {
106 | $line = trim($line);
107 |
108 | $pos = strpos($line, "=");
109 | if ($pos !== false)
110 | {
111 | $key = substr($line, 0, $pos);
112 | $val = (string)substr($line, $pos + 1);
113 |
114 | if (!isset($result["options"][$key])) $result["options"][$key] = $val;
115 | }
116 |
117 | if (feof($fp)) break;
118 | }
119 |
120 | fclose($fp);
121 |
122 | return $result;
123 | }
124 |
125 | public function AddAction($servicename, $actionname, $actiondesc, $phpfile, $args, $display = false)
126 | {
127 | $cmd = escapeshellarg($this->GetServiceManagerRealpath());
128 | $cmd .= " addaction " . escapeshellarg($servicename);
129 | $cmd .= " " . escapeshellarg($actionname);
130 | $cmd .= " " . escapeshellarg($actiondesc);
131 | $cmd .= " " . escapeshellarg($this->GetPHPBinary());
132 | $cmd .= " " . escapeshellarg($phpfile);
133 | foreach ($args as $arg) $cmd .= " " . escapeshellarg($arg);
134 |
135 | return $this->RunCommand($cmd, $display);
136 | }
137 |
138 | public function GetServiceManagerRealpath()
139 | {
140 | $os = php_uname("s");
141 |
142 | if (strtoupper(substr($os, 0, 3)) == "WIN") $result = $this->rootpath . "\\servicemanager.exe";
143 | else
144 | {
145 | if (file_exists($this->rootpath . "/servicemanager_nix")) $result = $this->rootpath . "/servicemanager_nix";
146 | else if (file_exists("/usr/local/bin/servicemanager")) $result = "/usr/local/bin/servicemanager";
147 | else if (strtoupper(substr($os, 0, 6)) == "DARWIN") $result = $this->rootpath . "/servicemanager_mac";
148 | else if (PHP_INT_SIZE >= 8) $result = $this->rootpath . "/servicemanager_nix_64";
149 | else $result = $this->rootpath . "/servicemanager_nix_32";
150 |
151 | @chmod($result, 0755);
152 | }
153 |
154 | return $result;
155 | }
156 |
157 | public function GetPHPBinary()
158 | {
159 | if (file_exists("/usr/bin/php") && realpath("/usr/bin/php") === PHP_BINARY) $result = "/usr/bin/php";
160 | else $result = PHP_BINARY;
161 |
162 | return $result;
163 | }
164 |
165 | public function RunCommand($cmd, $display = false)
166 | {
167 | if ($display) echo self::SMTranslate("Running command: %s", $cmd) . "\n\n";
168 |
169 | $fp = popen($cmd, "rb");
170 | if ($fp === false) return array("success" => false, "error" => self::SMTranslate("The executable failed to start."), "errorcode" => "start_process", "info" => $cmd);
171 |
172 | $data = "";
173 | while (($data2 = fread($fp, 65536)) !== false)
174 | {
175 | if ($display) echo $data2;
176 | $data .= $data2;
177 |
178 | if (feof($fp)) break;
179 | }
180 |
181 | pclose($fp);
182 |
183 | if ($display) echo "\n";
184 |
185 | return array("success" => true, "cmd" => $cmd, "output" => $data);
186 | }
187 |
188 | protected static function SMTranslate()
189 | {
190 | $args = func_get_args();
191 | if (!count($args)) return "";
192 |
193 | return call_user_func_array((defined("CS_TRANSLATE_FUNC") && function_exists(CS_TRANSLATE_FUNC) ? CS_TRANSLATE_FUNC : "sprintf"), $args);
194 | }
195 | }
196 | ?>
--------------------------------------------------------------------------------
/sdks/php/sdk_cloud_storage_server_files.php:
--------------------------------------------------------------------------------
1 | apiprefix = "/files/v1";
16 | }
17 |
18 | public function GetObjectByPath($path)
19 | {
20 | if (substr($path, 0, 1) !== "/") $path = "/" . $path;
21 |
22 | return $this->RunAPI("GET", "object/bypath" . $path);
23 | }
24 |
25 | public function GetRootFolderID()
26 | {
27 | return $this->RunAPI("GET", "user/root");
28 | }
29 |
30 | public function GetFolderList($folderid = "0")
31 | {
32 | $result = $this->RunAPI("GET", "folder/list/" . $folderid);
33 | if (!$result["success"]) return $result;
34 |
35 | $folders = array();
36 | $files = array();
37 | foreach ($result["body"]["items"] as $item)
38 | {
39 | if ($item["type"] === "file") $files[$item["name"]] = $item;
40 | else $folders[$item["name"]] = $item;
41 | }
42 |
43 | return array("success" => true, "id" => $folderid, "folders" => $folders, "files" => $files);
44 | }
45 |
46 | public function GetObjectIDByName($folderid, $name)
47 | {
48 | return $this->RunAPI("GET", "object/byname/" . $folderid . "/" . $name);
49 | }
50 |
51 | public function CreateFolder($folderid, $name)
52 | {
53 | $options = array(
54 | "folderid" => $folderid,
55 | "name" => $name
56 | );
57 |
58 | $result = $this->RunAPI("POST", "folder/create", $options);
59 | if (!$result["success"]) return $result;
60 |
61 | $result["id"] = $result["body"]["folder"]["id"];
62 |
63 | return $result;
64 | }
65 |
66 | public function GetTrashList($folderid = false)
67 | {
68 | return $this->RunAPI("GET", "trash/list" . ($folderid !== false ? "/" . $folderid : ""));
69 | }
70 |
71 | public function GetObjectByID($id)
72 | {
73 | return $this->RunAPI("GET", "object/byid/" . $id);
74 | }
75 |
76 | public function CopyObject($srcid, $destid)
77 | {
78 | $options = array(
79 | "srcid" => $srcid,
80 | "destid" => $destid
81 | );
82 |
83 | $result = $this->RunAPI("POST", "object/copy", $options);
84 | if (!$result["success"]) return $result;
85 |
86 | $result["id"] = $result["body"]["id"];
87 |
88 | return $result;
89 | }
90 |
91 | public function MoveObject($srcid, $destfolderid)
92 | {
93 | $options = array(
94 | "srcid" => $srcid,
95 | "destfolderid" => $destfolderid
96 | );
97 |
98 | return $this->RunAPI("POST", "object/move", $options);
99 | }
100 |
101 | public function RenameObject($id, $newname)
102 | {
103 | $options = array(
104 | "name" => $newname
105 | );
106 |
107 | return $this->RunAPI("POST", "object/rename/" . $id, $options);
108 | }
109 |
110 | public function TrashObject($id)
111 | {
112 | return $this->RunAPI("POST", "object/trash/" . $id);
113 | }
114 |
115 | public function RestoreObject($id)
116 | {
117 | return $this->RunAPI("POST", "object/restore/" . $id);
118 | }
119 |
120 | public function DeleteObject($id)
121 | {
122 | return $this->RunAPI("DELETE", "object/delete/" . $id);
123 | }
124 |
125 | public function GetUserLimits()
126 | {
127 | return $this->RunAPI("GET", "user/limits");
128 | }
129 |
130 | public function UploadFile($folderid, $destfilename, $data, $srcfilename, $fileid = false, $callback = false, $callbackopts = false)
131 | {
132 | // Determine if there is a file at the target already. It is more efficient than uploading and discovering afterwards.
133 | if ($fileid === false)
134 | {
135 | $result = $this->GetObjectIDByName($folderid, $destfilename);
136 | if (!$result["success"] && $result["errorcode"] !== "object_not_found") return $result;
137 |
138 | if ($result["success"] && $result["body"]["object"]["type"] !== "file") return array("success" => false, "error" => self::CSS_Translate("Parent folder already contains an object named '%s' that is not a file.", $destfilename), "errorcode" => "object_already_exists");
139 | }
140 |
141 | $fileinfo = array(
142 | "name" => "data",
143 | "filename" => $destfilename,
144 | "type" => "application/octet-stream"
145 | );
146 |
147 | if ($srcfilename !== false) $fileinfo["datafile"] = $srcfilename;
148 | else $fileinfo["data"] = $data;
149 |
150 | $options = array(
151 | "debug_callback" => $callback,
152 | "debug_callback_opts" => $callbackopts,
153 | "postvars" => array(
154 | "name" => $destfilename
155 | ),
156 | "files" => array($fileinfo)
157 | );
158 |
159 | return $this->RunAPI("POST", "file/upload/" . $folderid, $options, 200, false);
160 | }
161 |
162 | public function DownloadFile__Internal($response, $body, &$opts)
163 | {
164 | fwrite($opts["fp"], $body);
165 |
166 | if (is_callable($opts["callback"])) call_user_func_array($opts["callback"], array(&$opts));
167 |
168 | return true;
169 | }
170 |
171 | // Callback option only used when destination is a file.
172 | public function DownloadFile($destfileorfp, $fileid, $callback = false, $database = false)
173 | {
174 | if ($destfileorfp === false) $options = array();
175 | else
176 | {
177 | $fp = (is_resource($destfileorfp) ? $destfileorfp : fopen($destfileorfp, "wb"));
178 | if ($fp === false) return array("success" => false, "error" => self::CSS_Translate("Invalid destination filename or handle."), "errorcode" => "invalid_filename_or_handle");
179 |
180 | $options = array(
181 | "read_body_callback" => array($this, "DownloadFile__Internal"),
182 | "read_body_callback_opts" => array("fp" => $fp, "fileid" => $fileid, "callback" => $callback)
183 | );
184 | }
185 |
186 | if ($database) $result = $this->RunAPI("GET", "file/downloaddatabase", $options, 200, true, false);
187 | else $result = $this->RunAPI("GET", "file/download/" . $fileid, $options, 200, true, false);
188 |
189 | if ($destfileorfp !== false && !is_resource($destfileorfp)) fclose($fp);
190 |
191 | return $result;
192 | }
193 |
194 | public function CreateGuest($rootid, $read, $write, $delete, $expires)
195 | {
196 | $options = array(
197 | "rootid" => $rootid,
198 | "read" => (int)(bool)$read,
199 | "write" => (int)(bool)$write,
200 | "delete" => (int)(bool)$delete,
201 | "expires" => (int)$expires
202 | );
203 |
204 | return $this->RunAPI("POST", "guest/create", $options);
205 | }
206 |
207 | public function GetGuestList()
208 | {
209 | return $this->RunAPI("GET", "guest/list");
210 | }
211 |
212 | public function DeleteGuest($id)
213 | {
214 | return $this->RunAPI("DELETE", "guest/delete/" . $id);
215 | }
216 | }
217 | ?>
--------------------------------------------------------------------------------
/support/phpseclib/Crypt/AES.php:
--------------------------------------------------------------------------------
1 |
25 | * setKey('abcdefghijklmnop');
31 | *
32 | * $size = 10 * 1024;
33 | * $plaintext = '';
34 | * for ($i = 0; $i < $size; $i++) {
35 | * $plaintext.= 'a';
36 | * }
37 | *
38 | * echo $aes->decrypt($aes->encrypt($plaintext));
39 | * ?>
40 | *
41 | *
42 | * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
43 | * of this software and associated documentation files (the "Software"), to deal
44 | * in the Software without restriction, including without limitation the rights
45 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
46 | * copies of the Software, and to permit persons to whom the Software is
47 | * furnished to do so, subject to the following conditions:
48 | *
49 | * The above copyright notice and this permission notice shall be included in
50 | * all copies or substantial portions of the Software.
51 | *
52 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
53 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
54 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
55 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
56 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
57 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
58 | * THE SOFTWARE.
59 | *
60 | * @category Crypt
61 | * @package Crypt_AES
62 | * @author Jim Wigginton
63 | * @copyright 2008 Jim Wigginton
64 | * @license http://www.opensource.org/licenses/mit-license.html MIT License
65 | * @link http://phpseclib.sourceforge.net
66 | */
67 |
68 | /**
69 | * Include Crypt_Rijndael
70 | */
71 | if (!class_exists('Crypt_Rijndael')) {
72 | include_once 'Rijndael.php';
73 | }
74 |
75 | /**#@+
76 | * @access public
77 | * @see self::encrypt()
78 | * @see self::decrypt()
79 | */
80 | /**
81 | * Encrypt / decrypt using the Counter mode.
82 | *
83 | * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
84 | *
85 | * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
86 | */
87 | define('CRYPT_AES_MODE_CTR', CRYPT_MODE_CTR);
88 | /**
89 | * Encrypt / decrypt using the Electronic Code Book mode.
90 | *
91 | * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
92 | */
93 | define('CRYPT_AES_MODE_ECB', CRYPT_MODE_ECB);
94 | /**
95 | * Encrypt / decrypt using the Code Book Chaining mode.
96 | *
97 | * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
98 | */
99 | define('CRYPT_AES_MODE_CBC', CRYPT_MODE_CBC);
100 | /**
101 | * Encrypt / decrypt using the Cipher Feedback mode.
102 | *
103 | * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
104 | */
105 | define('CRYPT_AES_MODE_CFB', CRYPT_MODE_CFB);
106 | /**
107 | * Encrypt / decrypt using the Cipher Feedback mode.
108 | *
109 | * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
110 | */
111 | define('CRYPT_AES_MODE_OFB', CRYPT_MODE_OFB);
112 | /**#@-*/
113 |
114 | /**
115 | * Pure-PHP implementation of AES.
116 | *
117 | * @package Crypt_AES
118 | * @author Jim Wigginton
119 | * @access public
120 | */
121 | class Crypt_AES extends Crypt_Rijndael
122 | {
123 | /**
124 | * The namespace used by the cipher for its constants.
125 | *
126 | * @see Crypt_Base::const_namespace
127 | * @var string
128 | * @access private
129 | */
130 | var $const_namespace = 'AES';
131 |
132 | /**
133 | * Dummy function
134 | *
135 | * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
136 | *
137 | * @see Crypt_Rijndael::setBlockLength()
138 | * @access public
139 | * @param int $length
140 | */
141 | function setBlockLength($length)
142 | {
143 | return;
144 | }
145 |
146 | /**
147 | * Sets the key length
148 | *
149 | * Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to
150 | * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
151 | *
152 | * @see Crypt_Rijndael:setKeyLength()
153 | * @access public
154 | * @param int $length
155 | */
156 | function setKeyLength($length)
157 | {
158 | switch ($length) {
159 | case 160:
160 | $length = 192;
161 | break;
162 | case 224:
163 | $length = 256;
164 | }
165 | parent::setKeyLength($length);
166 | }
167 |
168 | /**
169 | * Sets the key.
170 | *
171 | * Rijndael supports five different key lengths, AES only supports three.
172 | *
173 | * @see Crypt_Rijndael:setKey()
174 | * @see setKeyLength()
175 | * @access public
176 | * @param string $key
177 | */
178 | function setKey($key)
179 | {
180 | parent::setKey($key);
181 |
182 | if (!$this->explicit_key_length) {
183 | $length = strlen($key);
184 | switch (true) {
185 | case $length <= 16:
186 | $this->key_length = 16;
187 | break;
188 | case $length <= 24:
189 | $this->key_length = 24;
190 | break;
191 | default:
192 | $this->key_length = 32;
193 | }
194 | $this->_setEngine();
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/support/remotedapi_web_server.php:
--------------------------------------------------------------------------------
1 | rws = false;
17 | $this->rhost = false;
18 | $this->rwr = false;
19 | $this->rwrclients = array();
20 | $this->rwrnextclientid = 1;
21 | }
22 |
23 | // Helper function to decide which class to use to handle the server.
24 | public static function IsRemoted($host)
25 | {
26 | return (strtolower(substr($host, 0, 6)) === "rws://" || strtolower(substr($host, 0, 7)) === "rwss://");
27 | }
28 |
29 | // Reconnects to the WebSocket.
30 | // This is a blocking call. In theory, that shouldn't cause too many problems.
31 | private function ReconnectRemotedAPIWebSocket()
32 | {
33 | if (!class_exists("HTTP", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/http.php";
34 | if (!class_exists("WebRoute", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/webroute.php";
35 |
36 | $url = $this->rhost;
37 | if ($url["scheme"] !== "rws" && $url["scheme"] !== "rwss") return array("success" => false, "error" => HTTP::HTTPTranslate("Invalid Remoted API URL scheme."), "errorcode" => "invalid_scheme");
38 | if ($url["loginusername"] === "") return array("success" => false, "error" => HTTP::HTTPTranslate("Remoted API URL is missing server key."), "errorcode" => "missing_server_key");
39 |
40 | $options = array("headers" => array());
41 | $options["headers"]["X-Remoted-APIKey"] = $url["loginusername"];
42 |
43 | $url["scheme"] = ($url["scheme"] === "rws" ? "ws" : "wss");
44 | unset($url["loginusername"]);
45 | unset($url["login"]);
46 |
47 | $url2 = "http://" . $url["host"];
48 |
49 | $url = HTTP::CondenseURL($url);
50 |
51 | return $this->rws->Connect($url, $url2, $options);
52 | }
53 |
54 | // Overrides the default behavior to start a server on a given host and port.
55 | // $host expected URL format: rws://serverapikey@host/webroutepath
56 | // Where 'serverapikey' is the Remoted API Server server API key and 'webroutepath' is the unique path to connect under.
57 | public function Start($host, $port, $sslopts = false)
58 | {
59 | $this->Stop();
60 |
61 | if (!class_exists("WebSocket", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/websocket.php";
62 | if (!class_exists("WebRoute", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/webroute.php";
63 |
64 | $this->rws = new WebSocket();
65 | $this->rhost = HTTP::ExtractURL($host);
66 |
67 | $this->rwr = new WebRoute();
68 |
69 | return $this->ReconnectRemotedAPIWebSocket();
70 | }
71 |
72 | public function Stop()
73 | {
74 | parent::Stop();
75 |
76 | if ($this->rws !== false) $this->rws->Disconnect();
77 |
78 | foreach ($this->rwrclients as $id => $client)
79 | {
80 | if ($client["state"]->webstate !== false && $client["state"]->webstate["httpstate"] !== false)
81 | {
82 | HTTP::ForceClose($client["state"]->webstate["httpstate"]);
83 | }
84 | }
85 |
86 | $this->rwrclients = array();
87 | }
88 |
89 | // Adds the WebSocket server stream and WebRoute clients.
90 | public function UpdateStreamsAndTimeout($prefix, &$timeout, &$readfps, &$writefps)
91 | {
92 | if ($this->rws !== false)
93 | {
94 | if ($this->rws->GetStream() === false) $this->ReconnectRemotedAPIWebSocket();
95 |
96 | if ($this->rws->GetStream() === false) $timeout = ($timeout === false || $timeout > 1 ? 1 : 0);
97 | else
98 | {
99 | $readfps[$prefix . "rws_http_s"] = $this->rws->GetStream();
100 |
101 | if ($this->rws->NeedsWrite()) $writefps[$prefix . "rws_http_s"] = $this->rws->GetStream();
102 | }
103 | }
104 |
105 | foreach ($this->rwrclients as $id => $client)
106 | {
107 | if ($client["state"]->webstate !== false && $client["state"]->webstate["httpstate"] !== false)
108 | {
109 | if (HTTP::WantRead($client["state"]->webstate["httpstate"])) $readfps[$prefix . "rws_http_c_" . $id] = $client["state"]->webstate["httpstate"]["fp"];
110 | if (HTTP::WantWrite($client["state"]->webstate["httpstate"])) $writefps[$prefix . "rws_http_c_" . $id] = $client["state"]->webstate["httpstate"]["fp"];
111 | }
112 | }
113 |
114 | parent::UpdateStreamsAndTimeout($prefix, $timeout, $readfps, $writefps);
115 | }
116 |
117 | protected function HandleNewConnections(&$readfps, &$writefps)
118 | {
119 | $result = $this->rws->ProcessQueuesAndTimeoutState(isset($readfps["rws_http_s"]), isset($writefps["rws_http_s"]));
120 | if ($result["success"])
121 | {
122 | // Initiate a new async WebRoute for each incoming connection request.
123 | $result = $this->rws->Read();
124 | while ($result["success"] && $result["data"] !== false)
125 | {
126 | $data = json_decode($result["data"]["payload"], true);
127 |
128 | if (isset($data["ipaddr"]) && isset($data["id"]) && isset($data["timeout"]))
129 | {
130 | $url = $this->rhost;
131 |
132 | $options = array(
133 | "async" => true,
134 | "headers" => array()
135 | );
136 |
137 | $options["headers"]["X-Remoted-APIKey"] = $url["loginusername"];
138 |
139 | $url["scheme"] = ($url["scheme"] === "rws" ? "wr" : "wrs");
140 | unset($url["loginusername"]);
141 | unset($url["login"]);
142 |
143 | $data["url"] = HTTP::CondenseURL($url);
144 | $data["retries"] = 3;
145 | $data["options"] = $options;
146 |
147 | // Due to the async setting, this will only initiate the connection. No data is actually sent/received at this point.
148 | $result = $this->rwr->Connect($data["url"], $data["id"], $data["timeout"], $data["options"]);
149 | if ($result["success"])
150 | {
151 | $result["data"] = $data;
152 |
153 | $this->rwrclients[$this->rwrnextclientid] = $result;
154 |
155 | $this->rwrnextclientid++;
156 | }
157 | }
158 |
159 | $result = $this->rws->Read();
160 | }
161 | }
162 |
163 | // WebSocket was disconnected due to a socket error.
164 | if (!$result["success"]) $this->rws->Disconnect();
165 |
166 | unset($readfps["rws_http_s"]);
167 | unset($writefps["rws_http_s"]);
168 |
169 | // Handle WebRoute clients.
170 | foreach ($this->rwrclients as $id => $client)
171 | {
172 | $result = $this->rwr->ProcessState($client["state"]);
173 | if ($result["success"])
174 | {
175 | // WebRoute successfully established. Convert it into a WebServer client.
176 | // At this point, the underlying WebServer class takes over.
177 | $client2 = $this->InitNewClient();
178 | $client2->fp = $result["fp"];
179 | $client2->ipaddr = $client["data"]["ipaddr"];
180 |
181 | // Remove the WebRoute client state.
182 | unset($this->rwrclients[$id]);
183 | }
184 | else if ($result["errorcode"] !== "no_data")
185 | {
186 | // Retry a few times in case of a flaky connection.
187 | $data = $client["data"];
188 | if ($data["retries"] > 0)
189 | {
190 | $data["retries"]--;
191 |
192 | $result = $this->rwr->Connect($data["url"], $data["id"], $data["timeout"], $data["options"]);
193 | if ($result["success"])
194 | {
195 | $result["data"] = $data;
196 |
197 | $this->rwrclients[$id] = $result;
198 | }
199 | }
200 | else
201 | {
202 | // Client connection failed.
203 | unset($this->rwrclients[$id]);
204 | }
205 | }
206 | }
207 | }
208 | }
209 | ?>
--------------------------------------------------------------------------------
/support/multi_async_helper.php:
--------------------------------------------------------------------------------
1 | objs = array();
12 | $this->queuedobjs = array();
13 | $this->limit = false;
14 | }
15 |
16 | public function SetConcurrencyLimit($limit)
17 | {
18 | $this->limit = $limit;
19 | }
20 |
21 | public function Set($key, $obj, $callback)
22 | {
23 | if (is_callable($callback))
24 | {
25 | $this->queuedobjs[$key] = array(
26 | "obj" => $obj,
27 | "callback" => $callback
28 | );
29 |
30 | unset($this->objs[$key]);
31 | }
32 | }
33 |
34 | public function NumObjects()
35 | {
36 | return count($this->queuedobjs) + count($this->objs);
37 | }
38 |
39 | public function GetObject($key)
40 | {
41 | if (isset($this->queuedobjs[$key])) $result = $this->queuedobjs[$key]["obj"];
42 | else if (isset($this->objs[$key])) $result = $this->objs[$key]["obj"];
43 | else $result = false;
44 |
45 | return $result;
46 | }
47 |
48 | // To be able to change a callback on the fly.
49 | public function SetCallback($key, $callback)
50 | {
51 | if (is_callable($callback))
52 | {
53 | if (isset($this->queuedobjs[$key])) $this->queuedobjs[$key]["callback"] = $callback;
54 | else if (isset($this->objs[$key])) $this->objs[$key]["callback"] = $callback;
55 | }
56 | }
57 |
58 | private function InternalDetach($key, $cleanup)
59 | {
60 | if (isset($this->queuedobjs[$key]))
61 | {
62 | call_user_func_array($this->queuedobjs[$key]["callback"], array("cleanup", &$cleanup, $key, &$this->queuedobjs[$key]["obj"]));
63 | $result = $this->queuedobjs[$key]["obj"];
64 | unset($this->queuedobjs[$key]);
65 | }
66 | else if (isset($this->objs[$key]))
67 | {
68 | call_user_func_array($this->objs[$key]["callback"], array("cleanup", &$cleanup, $key, &$this->objs[$key]["obj"]));
69 | $result = $this->objs[$key]["obj"];
70 | unset($this->objs[$key]);
71 | }
72 | else
73 | {
74 | $result = false;
75 | }
76 |
77 | return $result;
78 | }
79 |
80 | public function Detach($key)
81 | {
82 | return $this->InternalDetach($key, false);
83 | }
84 |
85 | public function Remove($key)
86 | {
87 | return $this->InternalDetach($key, true);
88 | }
89 |
90 | // A few default functions for direct file/socket handles.
91 | public static function ReadOnly($mode, &$data, $key, $fp)
92 | {
93 | switch ($mode)
94 | {
95 | case "init":
96 | case "update":
97 | {
98 | // Move to/Keep in the live queue.
99 | if (is_resource($fp)) $data = true;
100 |
101 | break;
102 | }
103 | case "read":
104 | case "write":
105 | case "writefps":
106 | {
107 | break;
108 | }
109 | case "readfps":
110 | {
111 | $data[$key] = $fp;
112 |
113 | break;
114 | }
115 | case "cleanup":
116 | {
117 | if ($data === true) @fclose($fp);
118 |
119 | break;
120 | }
121 | }
122 | }
123 |
124 | public static function WriteOnly($mode, &$data, $key, $fp)
125 | {
126 | switch ($mode)
127 | {
128 | case "init":
129 | case "update":
130 | {
131 | // Move to/Keep in the live queue.
132 | if (is_resource($fp)) $data = true;
133 |
134 | break;
135 | }
136 | case "read":
137 | case "readfps":
138 | case "write":
139 | {
140 | break;
141 | }
142 | case "writefps":
143 | {
144 | $data[$key] = $fp;
145 |
146 | break;
147 | }
148 | case "cleanup":
149 | {
150 | if ($data === true) @fclose($fp);
151 |
152 | break;
153 | }
154 | }
155 | }
156 |
157 | public static function ReadAndWrite($mode, &$data, $key, $fp)
158 | {
159 | switch ($mode)
160 | {
161 | case "init":
162 | case "update":
163 | {
164 | // Move to/Keep in the live queue.
165 | if (is_resource($fp)) $data = true;
166 |
167 | break;
168 | }
169 | case "read":
170 | case "write":
171 | {
172 | break;
173 | }
174 | case "readfps":
175 | case "writefps":
176 | {
177 | $data[$key] = $fp;
178 |
179 | break;
180 | }
181 | case "cleanup":
182 | {
183 | if ($data === true) @fclose($fp);
184 |
185 | break;
186 | }
187 | }
188 | }
189 |
190 | public function Wait($timeout = false)
191 | {
192 | // Move queued objects to live.
193 | $result2 = array("success" => true, "read" => array(), "write" => array(), "removed" => array(), "new" => array());
194 | while (count($this->queuedobjs) && ($this->limit === false || count($this->objs) < $this->limit))
195 | {
196 | $info = reset($this->queuedobjs);
197 | $key = key($this->queuedobjs);
198 | unset($this->queuedobjs[$key]);
199 |
200 | $result2["new"][$key] = $key;
201 |
202 | $keep = false;
203 | call_user_func_array($info["callback"], array("init", &$keep, $key, &$info["obj"]));
204 |
205 | $this->objs[$key] = $info;
206 |
207 | if (!$keep) $result2["removed"][$key] = $this->Remove($key);
208 | }
209 |
210 | // Walk the objects looking for read and write handles.
211 | $readfps = array();
212 | $writefps = array();
213 | $exceptfps = NULL;
214 | foreach ($this->objs as $key => &$info)
215 | {
216 | $keep = false;
217 | call_user_func_array($info["callback"], array("update", &$keep, $key, &$info["obj"]));
218 |
219 | if (!$keep) $result2["removed"][$key] = $this->Remove($key);
220 | else
221 | {
222 | call_user_func_array($info["callback"], array("readfps", &$readfps, $key, &$info["obj"]));
223 | call_user_func_array($info["callback"], array("writefps", &$writefps, $key, &$info["obj"]));
224 | }
225 | }
226 | if (!count($readfps)) $readfps = NULL;
227 | if (!count($writefps)) $writefps = NULL;
228 |
229 | // Wait for something to happen.
230 | if (isset($readfps) || isset($writefps))
231 | {
232 | if ($timeout === false) $timeout = NULL;
233 | $readfps2 = $readfps;
234 | $writefps2 = $writefps;
235 | $result = @stream_select($readfps, $writefps, $exceptfps, $timeout);
236 | if ($result === false) return array("success" => false, "error" => self::MAHTranslate("Wait() failed due to stream_select() failure. Most likely cause: Connection failure."), "errorcode" => "stream_select_failed");
237 | else if ($result > 0)
238 | {
239 | if (isset($readfps))
240 | {
241 | $readfps3 = array();
242 | foreach ($readfps as $key => $fp)
243 | {
244 | if (!isset($readfps2[$key]) || $readfps2[$key] !== $fp)
245 | {
246 | foreach ($readfps2 as $key2 => $fp2)
247 | {
248 | if ($fp === $fp2) $key = $key2;
249 | }
250 | }
251 |
252 | if (isset($this->objs[$key]))
253 | {
254 | call_user_func_array($this->objs[$key]["callback"], array("read", &$fp, $key, &$this->objs[$key]["obj"]));
255 |
256 | $readfps3[$key] = $fp;
257 | }
258 | }
259 |
260 | $result2["read"] = $readfps3;
261 | }
262 |
263 | if (isset($writefps))
264 | {
265 | $writefps3 = array();
266 | foreach ($writefps as $key => $fp)
267 | {
268 | if (!isset($writefps2[$key]) || $writefps2[$key] !== $fp)
269 | {
270 | foreach ($writefps2 as $key2 => $fp2)
271 | {
272 | if ($fp === $fp2) $key = $key2;
273 | }
274 | }
275 |
276 | if (isset($this->objs[$key]))
277 | {
278 | call_user_func_array($this->objs[$key]["callback"], array("write", &$fp, $key, &$this->objs[$key]["obj"]));
279 |
280 | $readfps3[$key] = $fp;
281 | }
282 | }
283 |
284 | $result2["write"] = $writefps3;
285 | }
286 | }
287 | }
288 |
289 | $result2["numleft"] = count($this->queuedobjs) + count($this->objs);
290 |
291 | return $result2;
292 | }
293 |
294 | public static function MAHTranslate()
295 | {
296 | $args = func_get_args();
297 | if (!count($args)) return "";
298 |
299 | return call_user_func_array((defined("CS_TRANSLATE_FUNC") && function_exists(CS_TRANSLATE_FUNC) ? CS_TRANSLATE_FUNC : "sprintf"), $args);
300 | }
301 | }
302 | ?>
--------------------------------------------------------------------------------
/support/webroute.php:
--------------------------------------------------------------------------------
1 | csprng = false;
17 | }
18 |
19 | public static function ProcessState($state)
20 | {
21 | while ($state->state !== "done")
22 | {
23 | switch ($state->state)
24 | {
25 | case "initialize":
26 | {
27 | $result = $state->web->Process($state->url, $state->options);
28 | if (!$result["success"]) return $result;
29 |
30 | if (isset($state->options["async"]) && $state->options["async"])
31 | {
32 | $state->async = true;
33 | $state->webstate = $result["state"];
34 |
35 | $state->state = "process_async";
36 | }
37 | else
38 | {
39 | $state->result = $result;
40 |
41 | $state->state = "post_retrieval";
42 | }
43 |
44 | break;
45 | }
46 | case "process_async":
47 | {
48 | // Run a cycle of the WebBrowser state processor.
49 | $result = $state->web->ProcessState($state->webstate);
50 | if (!$result["success"]) return $result;
51 |
52 | $state->webstate = false;
53 | $state->result = $result;
54 |
55 | $state->state = "post_retrieval";
56 |
57 | break;
58 | }
59 | case "post_retrieval":
60 | {
61 | if ($state->result["response"]["code"] != 101) return array("success" => false, "error" => self::WRTranslate("WebRoute::Connect() failed to connect to the WebRoute. Server returned: %s %s", $result["response"]["code"], $result["response"]["meaning"]), "errorcode" => "incorrect_server_response");
62 | if (!isset($state->result["headers"]["Sec-Webroute-Accept"])) return array("success" => false, "error" => self::WRTranslate("Server failed to include a 'Sec-WebRoute-Accept' header in its response to the request."), "errorcode" => "missing_server_webroute_accept_header");
63 |
64 | // Verify the Sec-WebRoute-Accept response.
65 | if ($state->result["headers"]["Sec-Webroute-Accept"][0] !== base64_encode(sha1($state->options["headers"]["WebRoute-ID"] . self::ID_GUID, true))) return array("success" => false, "error" => self::WRTranslate("The server's 'Sec-WebRoute-Accept' header is invalid."), "errorcode" => "invalid_server_webroute_accept_header");
66 |
67 | $state->state = "done";
68 |
69 | break;
70 | }
71 | }
72 | }
73 |
74 | return $state->result;
75 | }
76 |
77 | public function Connect($url, $id = false, $timeout = false, $options = array(), $web = false)
78 | {
79 | // Generate client ID.
80 | if ($id === false)
81 | {
82 | if (!class_exists("CSPRNG", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/random.php";
83 |
84 | if ($this->csprng === false) $this->csprng = new CSPRNG();
85 |
86 | $id = $this->csprng->GenerateString(64);
87 | }
88 |
89 | if (!class_exists("WebBrowser", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/web_browser.php";
90 |
91 | // Use WebBrowser to initiate the connection.
92 | if ($web === false) $web = new WebBrowser();
93 |
94 | // Transform URL.
95 | $url2 = HTTP::ExtractURL($url);
96 | if ($url2["scheme"] != "wr" && $url2["scheme"] != "wrs") return array("success" => false, "error" => self::WRTranslate("WebRoute::Connect() only supports the 'wr' and 'wrs' protocols."), "errorcode" => "protocol_check");
97 | $url2["scheme"] = str_replace("wr", "http", $url2["scheme"]);
98 | $url2 = HTTP::CondenseURL($url2);
99 |
100 | // Generate correct request headers.
101 | if (!isset($options["headers"])) $options["headers"] = array();
102 | $options["headers"]["Connection"] = "keep-alive, Upgrade";
103 | $options["headers"]["Pragma"] = "no-cache";
104 | $options["headers"]["WebRoute-Version"] = "1";
105 | $options["headers"]["WebRoute-ID"] = $id;
106 | if ($timeout !== false && is_int($timeout)) $options["headers"]["WebRoute-Timeout"] = (string)(int)$timeout;
107 | $options["headers"]["Upgrade"] = "webroute";
108 |
109 | // Initialize the process state object.
110 | $state = new stdClass();
111 | $state->async = false;
112 | $state->state = "initialize";
113 | $state->web = $web;
114 | $state->url = $url2;
115 | $state->options = $options;
116 | $state->webstate = false;
117 | $state->result = false;
118 |
119 | // Run at least one state cycle to finish initializing the state object.
120 | $result = $this->ProcessState($state);
121 |
122 | // Return the state for async calls. Caller must call ProcessState().
123 | if ($state->async) return array("success" => true, "id" => $id, "state" => $state);
124 |
125 | $result["id"] = $id;
126 |
127 | return $result;
128 | }
129 |
130 | // Implements the correct MultiAsyncHelper responses for WebRoute instances.
131 | public function ConnectAsync__Handler($mode, &$data, $key, $info)
132 | {
133 | switch ($mode)
134 | {
135 | case "init":
136 | {
137 | if ($info->init) $data = $info->keep;
138 | else
139 | {
140 | $info->result = $this->Connect($info->url, $info->id, $info->timeout, $info->options, $info->web);
141 | if (!$info->result["success"])
142 | {
143 | $info->keep = false;
144 |
145 | if (is_callable($info->callback)) call_user_func_array($info->callback, array($key, $info->url, $info->result));
146 | }
147 | else
148 | {
149 | $info->id = $info->result["id"];
150 | $info->state = $info->result["state"];
151 |
152 | // Move to the live queue.
153 | $data = true;
154 | }
155 | }
156 |
157 | break;
158 | }
159 | case "update":
160 | case "read":
161 | case "write":
162 | {
163 | if ($info->keep)
164 | {
165 | $info->result = $this->ProcessState($info->state);
166 | if ($info->result["success"] || $info->result["errorcode"] !== "no_data") $info->keep = false;
167 |
168 | if (is_callable($info->callback)) call_user_func_array($info->callback, array($key, $info->url, $info->result));
169 |
170 | if ($mode === "update") $data = $info->keep;
171 | }
172 |
173 | break;
174 | }
175 | case "readfps":
176 | {
177 | if ($info->state->webstate["httpstate"] !== false && HTTP::WantRead($info->state->webstate["httpstate"])) $data[$key] = $info->state->webstate["httpstate"]["fp"];
178 |
179 | break;
180 | }
181 | case "writefps":
182 | {
183 | if ($info->state->webstate["httpstate"] !== false && HTTP::WantWrite($info->state->webstate["httpstate"])) $data[$key] = $info->state->webstate["httpstate"]["fp"];
184 |
185 | break;
186 | }
187 | case "cleanup":
188 | {
189 | // When true, caller is removing. Otherwise, detaching from the queue.
190 | if ($data === true)
191 | {
192 | if (isset($info->state))
193 | {
194 | if ($info->state->webstate["httpstate"] !== false) HTTP::ForceClose($info->state->webstate["httpstate"]);
195 |
196 | unset($info->state);
197 | }
198 |
199 | $info->keep = false;
200 | }
201 |
202 | break;
203 | }
204 | }
205 | }
206 |
207 | public function ConnectAsync($helper, $key, $callback, $url, $id = false, $timeout = false, $options = array(), $web = false)
208 | {
209 | $options["async"] = true;
210 |
211 | $info = new stdClass();
212 | $info->init = false;
213 | $info->keep = true;
214 | $info->callback = $callback;
215 | $info->url = $url;
216 | $info->id = $id;
217 | $info->timeout = $timeout;
218 | $info->options = $options;
219 | $info->web = $web;
220 | $info->result = false;
221 |
222 | $helper->Set($key, $info, array($this, "ConnectAsync__Handler"));
223 |
224 | return array("success" => true);
225 | }
226 |
227 | public static function WRTranslate()
228 | {
229 | $args = func_get_args();
230 | if (!count($args)) return "";
231 |
232 | return call_user_func_array((defined("CS_TRANSLATE_FUNC") && function_exists(CS_TRANSLATE_FUNC) ? CS_TRANSLATE_FUNC : "sprintf"), $args);
233 | }
234 | }
235 | ?>
--------------------------------------------------------------------------------
/sdks/php/webroute.php:
--------------------------------------------------------------------------------
1 | csprng = false;
17 | }
18 |
19 | public static function ProcessState($state)
20 | {
21 | while ($state->state !== "done")
22 | {
23 | switch ($state->state)
24 | {
25 | case "initialize":
26 | {
27 | $result = $state->web->Process($state->url, $state->options);
28 | if (!$result["success"]) return $result;
29 |
30 | if (isset($state->options["async"]) && $state->options["async"])
31 | {
32 | $state->async = true;
33 | $state->webstate = $result["state"];
34 |
35 | $state->state = "process_async";
36 | }
37 | else
38 | {
39 | $state->result = $result;
40 |
41 | $state->state = "post_retrieval";
42 | }
43 |
44 | break;
45 | }
46 | case "process_async":
47 | {
48 | // Run a cycle of the WebBrowser state processor.
49 | $result = $state->web->ProcessState($state->webstate);
50 | if (!$result["success"]) return $result;
51 |
52 | $state->webstate = false;
53 | $state->result = $result;
54 |
55 | $state->state = "post_retrieval";
56 |
57 | break;
58 | }
59 | case "post_retrieval":
60 | {
61 | if ($state->result["response"]["code"] != 101) return array("success" => false, "error" => self::WRTranslate("WebRoute::Connect() failed to connect to the WebRoute. Server returned: %s %s", $result["response"]["code"], $result["response"]["meaning"]), "errorcode" => "incorrect_server_response");
62 | if (!isset($state->result["headers"]["Sec-Webroute-Accept"])) return array("success" => false, "error" => self::WRTranslate("Server failed to include a 'Sec-WebRoute-Accept' header in its response to the request."), "errorcode" => "missing_server_webroute_accept_header");
63 |
64 | // Verify the Sec-WebRoute-Accept response.
65 | if ($state->result["headers"]["Sec-Webroute-Accept"][0] !== base64_encode(sha1($state->options["headers"]["WebRoute-ID"] . self::ID_GUID, true))) return array("success" => false, "error" => self::WRTranslate("The server's 'Sec-WebRoute-Accept' header is invalid."), "errorcode" => "invalid_server_webroute_accept_header");
66 |
67 | $state->state = "done";
68 |
69 | break;
70 | }
71 | }
72 | }
73 |
74 | return $state->result;
75 | }
76 |
77 | public function Connect($url, $id = false, $timeout = false, $options = array(), $web = false)
78 | {
79 | // Generate client ID.
80 | if ($id === false)
81 | {
82 | if (!class_exists("CSPRNG", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/random.php";
83 |
84 | if ($this->csprng === false) $this->csprng = new CSPRNG();
85 |
86 | $id = $this->csprng->GenerateString(64);
87 | }
88 |
89 | if (!class_exists("WebBrowser", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/web_browser.php";
90 |
91 | // Use WebBrowser to initiate the connection.
92 | if ($web === false) $web = new WebBrowser();
93 |
94 | // Transform URL.
95 | $url2 = HTTP::ExtractURL($url);
96 | if ($url2["scheme"] != "wr" && $url2["scheme"] != "wrs") return array("success" => false, "error" => self::WRTranslate("WebRoute::Connect() only supports the 'wr' and 'wrs' protocols."), "errorcode" => "protocol_check");
97 | $url2["scheme"] = str_replace("wr", "http", $url2["scheme"]);
98 | $url2 = HTTP::CondenseURL($url2);
99 |
100 | // Generate correct request headers.
101 | if (!isset($options["headers"])) $options["headers"] = array();
102 | $options["headers"]["Connection"] = "keep-alive, Upgrade";
103 | $options["headers"]["Pragma"] = "no-cache";
104 | $options["headers"]["WebRoute-Version"] = "1";
105 | $options["headers"]["WebRoute-ID"] = $id;
106 | if ($timeout !== false && is_int($timeout)) $options["headers"]["WebRoute-Timeout"] = (string)(int)$timeout;
107 | $options["headers"]["Upgrade"] = "webroute";
108 |
109 | // Initialize the process state object.
110 | $state = new stdClass();
111 | $state->async = false;
112 | $state->state = "initialize";
113 | $state->web = $web;
114 | $state->url = $url2;
115 | $state->options = $options;
116 | $state->webstate = false;
117 | $state->result = false;
118 |
119 | // Run at least one state cycle to finish initializing the state object.
120 | $result = $this->ProcessState($state);
121 |
122 | // Return the state for async calls. Caller must call ProcessState().
123 | if ($state->async) return array("success" => true, "id" => $id, "state" => $state);
124 |
125 | $result["id"] = $id;
126 |
127 | return $result;
128 | }
129 |
130 | // Implements the correct MultiAsyncHelper responses for WebRoute instances.
131 | public function ConnectAsync__Handler($mode, &$data, $key, $info)
132 | {
133 | switch ($mode)
134 | {
135 | case "init":
136 | {
137 | if ($info->init) $data = $info->keep;
138 | else
139 | {
140 | $info->result = $this->Connect($info->url, $info->id, $info->timeout, $info->options, $info->web);
141 | if (!$info->result["success"])
142 | {
143 | $info->keep = false;
144 |
145 | if (is_callable($info->callback)) call_user_func_array($info->callback, array($key, $info->url, $info->result));
146 | }
147 | else
148 | {
149 | $info->id = $info->result["id"];
150 | $info->state = $info->result["state"];
151 |
152 | // Move to the live queue.
153 | $data = true;
154 | }
155 | }
156 |
157 | break;
158 | }
159 | case "update":
160 | case "read":
161 | case "write":
162 | {
163 | if ($info->keep)
164 | {
165 | $info->result = $this->ProcessState($info->state);
166 | if ($info->result["success"] || $info->result["errorcode"] !== "no_data") $info->keep = false;
167 |
168 | if (is_callable($info->callback)) call_user_func_array($info->callback, array($key, $info->url, $info->result));
169 |
170 | if ($mode === "update") $data = $info->keep;
171 | }
172 |
173 | break;
174 | }
175 | case "readfps":
176 | {
177 | if ($info->state->webstate["httpstate"] !== false && HTTP::WantRead($info->state->webstate["httpstate"])) $data[$key] = $info->state->webstate["httpstate"]["fp"];
178 |
179 | break;
180 | }
181 | case "writefps":
182 | {
183 | if ($info->state->webstate["httpstate"] !== false && HTTP::WantWrite($info->state->webstate["httpstate"])) $data[$key] = $info->state->webstate["httpstate"]["fp"];
184 |
185 | break;
186 | }
187 | case "cleanup":
188 | {
189 | // When true, caller is removing. Otherwise, detaching from the queue.
190 | if ($data === true)
191 | {
192 | if (isset($info->state))
193 | {
194 | if ($info->state->webstate["httpstate"] !== false) HTTP::ForceClose($info->state->webstate["httpstate"]);
195 |
196 | unset($info->state);
197 | }
198 |
199 | $info->keep = false;
200 | }
201 |
202 | break;
203 | }
204 | }
205 | }
206 |
207 | public function ConnectAsync($helper, $key, $callback, $url, $id = false, $timeout = false, $options = array(), $web = false)
208 | {
209 | $options["async"] = true;
210 |
211 | $info = new stdClass();
212 | $info->init = false;
213 | $info->keep = true;
214 | $info->callback = $callback;
215 | $info->url = $url;
216 | $info->id = $id;
217 | $info->timeout = $timeout;
218 | $info->options = $options;
219 | $info->web = $web;
220 | $info->result = false;
221 |
222 | $helper->Set($key, $info, array($this, "ConnectAsync__Handler"));
223 |
224 | return array("success" => true);
225 | }
226 |
227 | public static function WRTranslate()
228 | {
229 | $args = func_get_args();
230 | if (!count($args)) return "";
231 |
232 | return call_user_func_array((defined("CS_TRANSLATE_FUNC") && function_exists(CS_TRANSLATE_FUNC) ? CS_TRANSLATE_FUNC : "sprintf"), $args);
233 | }
234 | }
235 | ?>
--------------------------------------------------------------------------------
/manage.php:
--------------------------------------------------------------------------------
1 | array(
22 | "s" => "suppressoutput",
23 | "?" => "help"
24 | ),
25 | "rules" => array(
26 | "suppressoutput" => array("arg" => false),
27 | "help" => array("arg" => false)
28 | )
29 | );
30 | $args = CLI::ParseCommandLine($options);
31 |
32 | if (isset($args["opts"]["help"]))
33 | {
34 | echo "Cloud Storage Server management command-line tool\n";
35 | echo "Purpose: Manage users in the users database.\n";
36 | echo "\n";
37 | echo "This tool is question/answer enabled. Just running it will provide a guided interface. It can also be run entirely from the command-line if you know all the answers.\n";
38 | echo "\n";
39 | echo "Syntax: " . $args["file"] . " [options] [cmd [cmdoptions]]\n";
40 | echo "Options:\n";
41 | echo "\t-s Suppress most output. Useful for capturing JSON output.\n";
42 | echo "\n";
43 | echo "Examples:\n";
44 | echo "\tphp " . $args["file"] . "\n";
45 | echo "\tphp " . $args["file"] . " create username=test\n";
46 | echo "\tphp " . $args["file"] . " -s list\n";
47 | echo "\tphp " . $args["file"] . " -s get-info test\n";
48 |
49 | exit();
50 | }
51 |
52 | $suppressoutput = (isset($args["opts"]["suppressoutput"]) && $args["opts"]["suppressoutput"]);
53 |
54 | $config = CSS_LoadConfig();
55 |
56 | if (!isset($config["quota"])) CSS_DisplayError("Configuration is incomplete or missing. Run 'install.php' first.");
57 |
58 | require_once $rootpath . "/support/db.php";
59 | require_once $rootpath . "/support/db_sqlite.php";
60 |
61 | $db = new CSDB_sqlite();
62 |
63 | try
64 | {
65 | $db->Connect("sqlite:" . $rootpath . "/data/main.db");
66 | }
67 | catch (Exception $e)
68 | {
69 | CSS_DisplayError("Unable to connect to SQLite database. " . $e->getMessage());
70 | }
71 |
72 | if (!$db->TableExists("users")) CSS_DisplayError("Database table is missing. Run 'install.php' first.");
73 |
74 | require_once $rootpath . "/support/event_manager.php";
75 |
76 | $em = new EventManager();
77 |
78 | $userhelper = new CSS_UserHelper();
79 | $userhelper->Init($config, $db, $em);
80 |
81 | // Load all server extensions.
82 | $serverexts = CSS_LoadServerExtensions();
83 |
84 | // Let each server extension register event handler callbacks (e.g. "CSS_UserHelper::DeleteUser").
85 | // For most events, it is best to wait until later to do anything about changes (i.e. lazy updates).
86 | foreach ($serverexts as $serverext) $serverext->RegisterHandlers($em);
87 |
88 | // Get the command.
89 | $cmds = array("list" => "List all users", "create" => "Add a new user", "update" => "Change a user's transfer limit and quota.", "add-ext" => "Add user access to an API extension", "remove-ext" => "Remove user access to an API extension", "get-info" => "Get detailed information about a user", "delete" => "Delete a user");
90 |
91 | $cmd = CLI::GetLimitedUserInputWithArgs($args, false, "Command", false, "Available commands:", $cmds, true, $suppressoutput);
92 |
93 | function DisplayResult($result)
94 | {
95 | echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
96 |
97 | exit();
98 | }
99 |
100 | function FixUserRow($row)
101 | {
102 | $row->apikey .= "-" . $row->id;
103 | $row->totalbytes = (double)$row->totalbytes;
104 | $row->quota = (double)$row->quota;
105 | $row->transferstart = (int)$row->transferstart;
106 | $row->transferbytes = (double)$row->transferbytes;
107 | $row->transferlimit = (double)$row->transferlimit;
108 | $row->lastupdated = (int)$row->lastupdated;
109 | $row->quotaleft = CSS_GetQuotaLeft($row);
110 | $row->transferleft = CSS_GetTransferLeft($row);
111 | }
112 |
113 | function GetUserList()
114 | {
115 | global $db;
116 |
117 | $result = array("success" => true, "users" => array());
118 |
119 | $result2 = $db->Query("SELECT", array(
120 | "*",
121 | "FROM" => "?",
122 | ), "users");
123 |
124 | while ($row = $result2->NextRow())
125 | {
126 | $row->serverexts = json_decode($row->serverexts, true);
127 |
128 | FixUserRow($row);
129 |
130 | $result["users"][$row->username] = (array)$row;
131 | }
132 |
133 | ksort($result["users"]);
134 |
135 | return $result;
136 | }
137 |
138 | function GetUser()
139 | {
140 | global $suppressoutput, $args, $userhelper;
141 |
142 | if ($suppressoutput || CLI::CanGetUserInputWithArgs($args, "username"))
143 | {
144 | $username = CLI::GetUserInputWithArgs($args, "username", "Username", false, "", $suppressoutput);
145 |
146 | $result = $userhelper->GetUserByUsername($username);
147 | if (!$result["success"]) DisplayResult($result);
148 |
149 | FixUserRow($result["info"]);
150 |
151 | $result["info"] = (array)$result["info"];
152 | }
153 | else
154 | {
155 | $result = GetUserList();
156 | if (!$result["success"]) DisplayResult($result);
157 |
158 | $users = array();
159 | foreach ($result["users"] as $username => $user)
160 | {
161 | $options = array();
162 | if (!count($user["serverexts"])) $options[] = "No extensions enabled";
163 | else $options[] = (count($user["serverexts"]) == 1 ? "1 extension" : count($user["serverexts"]) . " extensions") . " enabled (" . implode(", ", array_keys($user["serverexts"])) . ")";
164 |
165 | if ($user["quotaleft"] > -1) $options[] = Str::ConvertBytesToUserStr($user["quotaleft"]) . " storage left";
166 | if ($user["transferleft"] > -1) $options[] = Str::ConvertBytesToUserStr($user["transferleft"]) . " transfer left";
167 |
168 | $users[$username] = implode(", ", $options);
169 | }
170 | if (!count($users)) CLI::DisplayError("No users have been created. Try creating your first user with the command: create");
171 | $username = CLI::GetLimitedUserInputWithArgs($args, "username", "Username", false, "Available users:", $users, true, $suppressoutput);
172 | $result["info"] = $result["users"][$username];
173 | unset($result["users"]);
174 | }
175 |
176 | return $result;
177 | }
178 |
179 | if ($cmd === "list")
180 | {
181 | DisplayResult(GetUserList());
182 | }
183 | else if ($cmd === "create")
184 | {
185 | CLI::ReinitArgs($args, array("username", "basepath", "quota", "transferlimit"));
186 |
187 | // Get the username of the new user.
188 | do
189 | {
190 | $username = CLI::GetUserInputWithArgs($args, "username", "Username", false, "", $suppressoutput);
191 |
192 | $result = $userhelper->GetUserByUsername($username);
193 | if ($result["success"]) CLI::DisplayError("A user with the username '" . $username . "' already exists.", false, false);
194 | else if ($result["errorcode"] !== "no_user") CLI::DisplayError("An error occurred while attempting to check the database.", $result);
195 | else break;
196 | } while (1);
197 |
198 | $options = array();
199 | $options["basepath"] = CLI::GetUserInputWithArgs($args, "basepath", "Base path", $config["basepath"], "", $suppressoutput);
200 | $options["quota"] = CSS_ConvertUserStrToBytes(CLI::GetUserInputWithArgs($args, "quota", "Quota", $config["quota"], "", $suppressoutput));
201 | $options["transferlimit"] = CSS_ConvertUserStrToBytes(CLI::GetUserInputWithArgs($args, "transferlimit", "Transfer limit", $config["transferlimit"], "", $suppressoutput));
202 |
203 | $result = $userhelper->CreateUser($username, $options);
204 | if (!$result["success"]) DisplayResult($result);
205 |
206 | FixUserRow($result["info"]);
207 |
208 | DisplayResult($result);
209 | }
210 | else if ($cmd === "update")
211 | {
212 | CLI::ReinitArgs($args, array("username", "quota", "transferlimit"));
213 |
214 | $result = GetUser();
215 | $id = $result["info"]["id"];
216 |
217 | $options = array();
218 | $options["quota"] = CSS_ConvertUserStrToBytes(CLI::GetUserInputWithArgs($args, "quota", "Quota", Str::ConvertBytesToUserStr($result["info"]["quota"]), "", $suppressoutput));
219 | $options["transferlimit"] = CSS_ConvertUserStrToBytes(CLI::GetUserInputWithArgs($args, "transferlimit", "Transfer limit", Str::ConvertBytesToUserStr($result["info"]["transferlimit"]), "", $suppressoutput));
220 |
221 | $result = $userhelper->UpdateUser($id, $options);
222 | if (!$result["success"]) DisplayResult($result);
223 |
224 | $result = $userhelper->GetUserByID($id);
225 | if (!$result["success"]) DisplayResult($result);
226 |
227 | FixUserRow($result["info"]);
228 |
229 | DisplayResult($result);
230 | }
231 | else if ($cmd === "add-ext")
232 | {
233 | CLI::ReinitArgs($args, array("username", "extension"));
234 |
235 | $result = GetUser();
236 | $id = $result["info"]["id"];
237 |
238 | $extensions = array();
239 | foreach ($serverexts as $name => $serverext) $extensions[$name] = "/" . $name;
240 | $extension = CLI::GetLimitedUserInputWithArgs($args, "extension", "Extension", false, "Available extensions:", $extensions, true, $suppressoutput);
241 |
242 | $result2 = $serverexts[$extension]->AddUserExtension((object)$result["info"]);
243 | if (!$result2["success"]) DisplayResult($result2);
244 |
245 | $options = array();
246 | $options["serverexts"] = $result["info"]["serverexts"];
247 | $options["serverexts"][$extension] = $result2["info"];
248 |
249 | $result = $userhelper->UpdateUser($id, $options);
250 | if (!$result["success"]) DisplayResult($result);
251 |
252 | $result = $userhelper->GetUserByID($id);
253 | if (!$result["success"]) DisplayResult($result);
254 |
255 | FixUserRow($result["info"]);
256 |
257 | DisplayResult($result);
258 | }
259 | else if ($cmd === "remove-ext")
260 | {
261 | CLI::ReinitArgs($args, array("username", "extension"));
262 |
263 | $result = GetUser();
264 | $id = $result["info"]["id"];
265 |
266 | $extensions = array();
267 | foreach ($result["info"]["serverexts"] as $name => $info) $extensions[$name] = "/" . $name . " - " . json_encode($info, JSON_UNESCAPED_SLASHES);
268 | if (!count($extensions)) CLI::DisplayError("No extensions have been enabled for the user.");
269 | $extension = CLI::GetLimitedUserInputWithArgs($args, "extension", "Extension", false, "Enabled extensions:", $extensions, true, $suppressoutput);
270 |
271 | $options = array();
272 | $options["serverexts"] = $result["info"]["serverexts"];
273 | unset($options["serverexts"][$extension]);
274 |
275 | $result = $userhelper->UpdateUser($id, $options);
276 | if (!$result["success"]) DisplayResult($result);
277 |
278 | $result = $userhelper->GetUserByID($id);
279 | if (!$result["success"]) DisplayResult($result);
280 |
281 | FixUserRow($result["info"]);
282 |
283 | DisplayResult($result);
284 | }
285 | else if ($cmd === "get-info")
286 | {
287 | CLI::ReinitArgs($args, array("username"));
288 |
289 | DisplayResult(GetUser());
290 | }
291 | else if ($cmd === "delete")
292 | {
293 | CLI::ReinitArgs($args, array("username"));
294 |
295 | $result = GetUser();
296 | $id = $result["info"]["id"];
297 |
298 | $result = $userhelper->DeleteUser($id);
299 |
300 | DisplayResult($result);
301 | }
302 | ?>
--------------------------------------------------------------------------------
/sdks/php/deflate_stream.php:
--------------------------------------------------------------------------------
1 | open = false;
13 | }
14 |
15 | public function __destruct()
16 | {
17 | $this->Finalize();
18 | }
19 |
20 | public static function IsSupported()
21 | {
22 | if (!is_bool(self::$supported))
23 | {
24 | self::$supported = function_exists("stream_filter_append") && function_exists("stream_filter_remove") && function_exists("gzcompress");
25 | if (self::$supported)
26 | {
27 | $data = self::Compress("test");
28 | if ($data === false || $data === "") self::$supported = false;
29 | else
30 | {
31 | $data = self::Uncompress($data);
32 | if ($data === false || $data !== "test") self::$supported = false;
33 | }
34 | }
35 | }
36 |
37 | return self::$supported;
38 | }
39 |
40 | public static function Compress($data, $compresslevel = -1, $options = array())
41 | {
42 | $ds = new DeflateStream;
43 | if (!$ds->Init("wb", $compresslevel, $options)) return false;
44 | if (!$ds->Write($data)) return false;
45 | if (!$ds->Finalize()) return false;
46 | $data = $ds->Read();
47 |
48 | return $data;
49 | }
50 |
51 | public static function Uncompress($data, $options = array("type" => "auto"))
52 | {
53 | $ds = new DeflateStream;
54 | if (!$ds->Init("rb", -1, $options)) return false;
55 | if (!$ds->Write($data)) return false;
56 | if (!$ds->Finalize()) return false;
57 | $data = $ds->Read();
58 |
59 | return $data;
60 | }
61 |
62 | public function Init($mode, $compresslevel = -1, $options = array())
63 | {
64 | if ($mode !== "rb" && $mode !== "wb") return false;
65 | if ($this->open) $this->Finalize();
66 |
67 | $this->fp = fopen("php://memory", "w+b");
68 | if ($this->fp === false) return false;
69 | $this->compress = ($mode == "wb");
70 | if (!isset($options["type"])) $options["type"] = "rfc1951";
71 |
72 | if ($options["type"] == "rfc1950") $options["type"] = "zlib";
73 | else if ($options["type"] == "rfc1952") $options["type"] = "gzip";
74 |
75 | if ($options["type"] != "zlib" && $options["type"] != "gzip" && ($this->compress || $options["type"] != "auto")) $options["type"] = "raw";
76 | $this->options = $options;
77 |
78 | // Add the deflate filter.
79 | if ($this->compress) $this->filter = stream_filter_append($this->fp, "zlib.deflate", STREAM_FILTER_WRITE, $compresslevel);
80 | else $this->filter = stream_filter_append($this->fp, "zlib.inflate", STREAM_FILTER_READ);
81 |
82 | $this->open = true;
83 | $this->indata = "";
84 | $this->outdata = "";
85 |
86 | if ($this->compress)
87 | {
88 | if ($this->options["type"] == "zlib")
89 | {
90 | $this->outdata .= "\x78\x9C";
91 | $this->options["a"] = 1;
92 | $this->options["b"] = 0;
93 | }
94 | else if ($this->options["type"] == "gzip")
95 | {
96 | if (!class_exists("CRC32Stream", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/crc32_stream.php";
97 |
98 | $this->options["crc32"] = new CRC32Stream();
99 | $this->options["crc32"]->Init();
100 | $this->options["bytes"] = 0;
101 |
102 | $this->outdata .= "\x1F\x8B\x08";
103 | $flags = 0;
104 | if (isset($this->options["filename"])) $flags |= 0x08;
105 | if (isset($this->options["comment"])) $flags |= 0x10;
106 | $this->outdata .= chr($flags);
107 | $this->outdata .= "\x00\x00\x00\x00";
108 | $this->outdata .= "\x00";
109 | $this->outdata .= "\x03";
110 |
111 | if (isset($this->options["filename"])) $this->outdata .= str_replace("\x00", " ", $this->options["filename"]) . "\x00";
112 | if (isset($this->options["comment"])) $this->outdata .= str_replace("\x00", " ", $this->options["comment"]) . "\x00";
113 | }
114 | }
115 | else
116 | {
117 | $this->options["header"] = false;
118 | }
119 |
120 | return true;
121 | }
122 |
123 | public function Read()
124 | {
125 | $result = $this->outdata;
126 | $this->outdata = "";
127 |
128 | return $result;
129 | }
130 |
131 | public function Write($data)
132 | {
133 | if (!$this->open) return false;
134 |
135 | if ($this->compress)
136 | {
137 | if ($this->options["type"] == "zlib")
138 | {
139 | // Adler-32.
140 | $y = strlen($data);
141 | for ($x = 0; $x < $y; $x++)
142 | {
143 | $this->options["a"] = ($this->options["a"] + ord($data[$x])) % 65521;
144 | $this->options["b"] = ($this->options["b"] + $this->options["a"]) % 65521;
145 | }
146 | }
147 | else if ($this->options["type"] == "gzip")
148 | {
149 | $this->options["crc32"]->AddData($data);
150 | $this->options["bytes"] = $this->ADD32($this->options["bytes"], strlen($data));
151 | }
152 |
153 | $this->indata .= $data;
154 | while (strlen($this->indata) >= 65536)
155 | {
156 | fwrite($this->fp, substr($this->indata, 0, 65536));
157 | $this->indata = substr($this->indata, 65536);
158 |
159 | $this->ProcessOutput();
160 | }
161 | }
162 | else
163 | {
164 | $this->indata .= $data;
165 | $this->ProcessInput();
166 | }
167 |
168 | return true;
169 | }
170 |
171 | // Finalizes the stream.
172 | public function Finalize()
173 | {
174 | if (!$this->open) return false;
175 |
176 | if (!$this->compress) $this->ProcessInput(true);
177 |
178 | if (strlen($this->indata) > 0)
179 | {
180 | fwrite($this->fp, $this->indata);
181 | $this->indata = "";
182 | }
183 |
184 | // Removing the filter pushes the last buffer into the stream.
185 | stream_filter_remove($this->filter);
186 | $this->filter = false;
187 |
188 | $this->ProcessOutput();
189 |
190 | fclose($this->fp);
191 |
192 | if ($this->compress)
193 | {
194 | if ($this->options["type"] == "zlib") $this->outdata .= pack("N", $this->SHL32($this->options["b"], 16) | $this->options["a"]);
195 | else if ($this->options["type"] == "gzip") $this->outdata .= pack("V", $this->options["crc32"]->Finalize()) . pack("V", $this->options["bytes"]);
196 | }
197 |
198 | $this->open = false;
199 |
200 | return true;
201 | }
202 |
203 | private function ProcessOutput()
204 | {
205 | rewind($this->fp);
206 |
207 | // Hack! Because ftell() on a stream with a filter is still broken even under the latest PHP a mere 11 years later.
208 | // See: https://bugs.php.net/bug.php?id=49874
209 | ob_start();
210 | fpassthru($this->fp);
211 | $this->outdata .= ob_get_contents();
212 | ob_end_clean();
213 |
214 | rewind($this->fp);
215 | ftruncate($this->fp, 0);
216 | }
217 |
218 | private function ProcessInput($final = false)
219 | {
220 | // Automatically determine the type of data based on the header signature.
221 | if ($this->options["type"] == "auto")
222 | {
223 | if (strlen($this->indata) >= 3)
224 | {
225 | $zlibtest = unpack("n", substr($this->indata, 0, 2));
226 |
227 | if (substr($this->indata, 0, 3) === "\x1F\x8B\x08") $this->options["type"] = "gzip";
228 | else if ((ord($this->indata[0]) & 0x0F) == 8 && ((ord($this->indata[0]) & 0xF0) >> 4) < 8 && $zlibtest[1] % 31 == 0) $this->options["type"] = "zlib";
229 | else $this->options["type"] = "raw";
230 | }
231 | else if ($final) $this->options["type"] = "raw";
232 | }
233 |
234 | if ($this->options["type"] == "gzip")
235 | {
236 | if (!$this->options["header"])
237 | {
238 | if (strlen($this->indata) >= 10)
239 | {
240 | $idcm = substr($this->indata, 0, 3);
241 | $flg = ord($this->indata[3]);
242 |
243 | if ($idcm !== "\x1F\x8B\x08") $this->options["type"] = "ignore";
244 | else
245 | {
246 | // Calculate the number of bytes to skip. If flags are set, the size can be dynamic.
247 | $size = 10;
248 | $y = strlen($this->indata);
249 |
250 | // FLG.FEXTRA
251 | if ($size && ($flg & 0x04))
252 | {
253 | if ($size + 2 >= $y) $size = 0;
254 | else
255 | {
256 | $xlen = unpack("v", substr($this->indata, $size, 2));
257 | $size = ($size + 2 + $xlen <= $y ? $size + 2 + $xlen : 0);
258 | }
259 | }
260 |
261 | // FLG.FNAME
262 | if ($size && ($flg & 0x08))
263 | {
264 | $pos = strpos($this->indata, "\x00", $size);
265 | $size = ($pos !== false ? $pos + 1 : 0);
266 | }
267 |
268 | // FLG.FCOMMENT
269 | if ($size && ($flg & 0x10))
270 | {
271 | $pos = strpos($this->indata, "\x00", $size);
272 | $size = ($pos !== false ? $pos + 1 : 0);
273 | }
274 |
275 | // FLG.FHCRC
276 | if ($size && ($flg & 0x02)) $size = ($size + 2 <= $y ? $size + 2 : 0);
277 |
278 | if ($size)
279 | {
280 | $this->indata = substr($this->indata, $size);
281 | $this->options["header"] = true;
282 | }
283 | }
284 | }
285 | }
286 |
287 | if ($this->options["header"] && strlen($this->indata) > 8)
288 | {
289 | fwrite($this->fp, substr($this->indata, 0, -8));
290 | $this->indata = substr($this->indata, -8);
291 |
292 | $this->ProcessOutput();
293 | }
294 |
295 | if ($final) $this->indata = "";
296 | }
297 | else if ($this->options["type"] == "zlib")
298 | {
299 | if (!$this->options["header"])
300 | {
301 | if (strlen($this->indata) >= 2)
302 | {
303 | $cmf = ord($this->indata[0]);
304 | $flg = ord($this->indata[1]);
305 | $cm = $cmf & 0x0F;
306 | $cinfo = ($cmf & 0xF0) >> 4;
307 |
308 | // Compression method 'deflate' ($cm = 8), window size - 8 ($cinfo < 8), no preset dictionaries ($flg bit 5), checksum validates.
309 | if ($cm != 8 || $cinfo > 7 || ($flg & 0x20) || (($cmf << 8 | $flg) % 31) != 0) $this->options["type"] = "ignore";
310 | else
311 | {
312 | $this->indata = substr($this->indata, 2);
313 | $this->options["header"] = true;
314 | }
315 | }
316 | }
317 |
318 | if ($this->options["header"] && strlen($this->indata) > 4)
319 | {
320 | fwrite($this->fp, substr($this->indata, 0, -4));
321 | $this->indata = substr($this->indata, -4);
322 |
323 | $this->ProcessOutput();
324 | }
325 |
326 | if ($final) $this->indata = "";
327 | }
328 |
329 | if ($this->options["type"] == "raw")
330 | {
331 | fwrite($this->fp, $this->indata);
332 | $this->indata = "";
333 |
334 | $this->ProcessOutput();
335 | }
336 |
337 | // Only set when an unrecoverable header error has occurred for gzip or zlib.
338 | if ($this->options["type"] == "ignore") $this->indata = "";
339 | }
340 |
341 | private function SHL32($num, $bits)
342 | {
343 | if ($bits < 0) $bits = 0;
344 |
345 | return $this->LIM32((int)$num << $bits);
346 | }
347 |
348 | private function LIM32($num)
349 | {
350 | return (int)((int)$num & 0xFFFFFFFF);
351 | }
352 |
353 | private function ADD32($num, $num2)
354 | {
355 | $num = (int)$num;
356 | $num2 = (int)$num2;
357 | $add = ((($num >> 30) & 0x03) + (($num2 >> 30) & 0x03));
358 | $num = ((int)($num & 0x3FFFFFFF) + (int)($num2 & 0x3FFFFFFF));
359 | if ($num & 0x40000000) $add++;
360 | $num = (int)(($num & 0x3FFFFFFF) | (($add & 0x03) << 30));
361 |
362 | return $num;
363 | }
364 | }
365 | ?>
--------------------------------------------------------------------------------
/support/deflate_stream.php:
--------------------------------------------------------------------------------
1 | open = false;
13 | }
14 |
15 | public function __destruct()
16 | {
17 | $this->Finalize();
18 | }
19 |
20 | public static function IsSupported()
21 | {
22 | if (!is_bool(self::$supported))
23 | {
24 | self::$supported = function_exists("stream_filter_append") && function_exists("stream_filter_remove") && function_exists("gzcompress");
25 | if (self::$supported)
26 | {
27 | $data = self::Compress("test");
28 | if ($data === false || $data === "") self::$supported = false;
29 | else
30 | {
31 | $data = self::Uncompress($data);
32 | if ($data === false || $data !== "test") self::$supported = false;
33 | }
34 | }
35 | }
36 |
37 | return self::$supported;
38 | }
39 |
40 | public static function Compress($data, $compresslevel = -1, $options = array())
41 | {
42 | $ds = new DeflateStream;
43 | if (!$ds->Init("wb", $compresslevel, $options)) return false;
44 | if (!$ds->Write($data)) return false;
45 | if (!$ds->Finalize()) return false;
46 | $data = $ds->Read();
47 |
48 | return $data;
49 | }
50 |
51 | public static function Uncompress($data, $options = array("type" => "auto"))
52 | {
53 | $ds = new DeflateStream;
54 | if (!$ds->Init("rb", -1, $options)) return false;
55 | if (!$ds->Write($data)) return false;
56 | if (!$ds->Finalize()) return false;
57 | $data = $ds->Read();
58 |
59 | return $data;
60 | }
61 |
62 | public function Init($mode, $compresslevel = -1, $options = array())
63 | {
64 | if ($mode !== "rb" && $mode !== "wb") return false;
65 | if ($this->open) $this->Finalize();
66 |
67 | $this->fp = fopen("php://memory", "w+b");
68 | if ($this->fp === false) return false;
69 | $this->compress = ($mode == "wb");
70 | if (!isset($options["type"])) $options["type"] = "rfc1951";
71 |
72 | if ($options["type"] == "rfc1950") $options["type"] = "zlib";
73 | else if ($options["type"] == "rfc1952") $options["type"] = "gzip";
74 |
75 | if ($options["type"] != "zlib" && $options["type"] != "gzip" && ($this->compress || $options["type"] != "auto")) $options["type"] = "raw";
76 | $this->options = $options;
77 |
78 | // Add the deflate filter.
79 | if ($this->compress) $this->filter = stream_filter_append($this->fp, "zlib.deflate", STREAM_FILTER_WRITE, $compresslevel);
80 | else $this->filter = stream_filter_append($this->fp, "zlib.inflate", STREAM_FILTER_READ);
81 |
82 | $this->open = true;
83 | $this->indata = "";
84 | $this->outdata = "";
85 |
86 | if ($this->compress)
87 | {
88 | if ($this->options["type"] == "zlib")
89 | {
90 | $this->outdata .= "\x78\x9C";
91 | $this->options["a"] = 1;
92 | $this->options["b"] = 0;
93 | }
94 | else if ($this->options["type"] == "gzip")
95 | {
96 | if (!class_exists("CRC32Stream", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/crc32_stream.php";
97 |
98 | $this->options["crc32"] = new CRC32Stream();
99 | $this->options["crc32"]->Init();
100 | $this->options["bytes"] = 0;
101 |
102 | $this->outdata .= "\x1F\x8B\x08";
103 | $flags = 0;
104 | if (isset($this->options["filename"])) $flags |= 0x08;
105 | if (isset($this->options["comment"])) $flags |= 0x10;
106 | $this->outdata .= chr($flags);
107 | $this->outdata .= "\x00\x00\x00\x00";
108 | $this->outdata .= "\x00";
109 | $this->outdata .= "\x03";
110 |
111 | if (isset($this->options["filename"])) $this->outdata .= str_replace("\x00", " ", $this->options["filename"]) . "\x00";
112 | if (isset($this->options["comment"])) $this->outdata .= str_replace("\x00", " ", $this->options["comment"]) . "\x00";
113 | }
114 | }
115 | else
116 | {
117 | $this->options["header"] = false;
118 | }
119 |
120 | return true;
121 | }
122 |
123 | public function Read()
124 | {
125 | $result = $this->outdata;
126 | $this->outdata = "";
127 |
128 | return $result;
129 | }
130 |
131 | public function Write($data)
132 | {
133 | if (!$this->open) return false;
134 |
135 | if ($this->compress)
136 | {
137 | if ($this->options["type"] == "zlib")
138 | {
139 | // Adler-32.
140 | $y = strlen($data);
141 | for ($x = 0; $x < $y; $x++)
142 | {
143 | $this->options["a"] = ($this->options["a"] + ord($data[$x])) % 65521;
144 | $this->options["b"] = ($this->options["b"] + $this->options["a"]) % 65521;
145 | }
146 | }
147 | else if ($this->options["type"] == "gzip")
148 | {
149 | $this->options["crc32"]->AddData($data);
150 | $this->options["bytes"] = $this->ADD32($this->options["bytes"], strlen($data));
151 | }
152 |
153 | $this->indata .= $data;
154 | while (strlen($this->indata) >= 65536)
155 | {
156 | fwrite($this->fp, substr($this->indata, 0, 65536));
157 | $this->indata = substr($this->indata, 65536);
158 |
159 | $this->ProcessOutput();
160 | }
161 | }
162 | else
163 | {
164 | $this->indata .= $data;
165 | $this->ProcessInput();
166 | }
167 |
168 | return true;
169 | }
170 |
171 | // Finalizes the stream.
172 | public function Finalize()
173 | {
174 | if (!$this->open) return false;
175 |
176 | if (!$this->compress) $this->ProcessInput(true);
177 |
178 | if (strlen($this->indata) > 0)
179 | {
180 | fwrite($this->fp, $this->indata);
181 | $this->indata = "";
182 | }
183 |
184 | // Removing the filter pushes the last buffer into the stream.
185 | stream_filter_remove($this->filter);
186 | $this->filter = false;
187 |
188 | $this->ProcessOutput();
189 |
190 | fclose($this->fp);
191 |
192 | if ($this->compress)
193 | {
194 | if ($this->options["type"] == "zlib") $this->outdata .= pack("N", $this->SHL32($this->options["b"], 16) | $this->options["a"]);
195 | else if ($this->options["type"] == "gzip") $this->outdata .= pack("V", $this->options["crc32"]->Finalize()) . pack("V", $this->options["bytes"]);
196 | }
197 |
198 | $this->open = false;
199 |
200 | return true;
201 | }
202 |
203 | private function ProcessOutput()
204 | {
205 | rewind($this->fp);
206 |
207 | // Hack! Because ftell() on a stream with a filter is still broken even under the latest PHP a mere 11 years later.
208 | // See: https://bugs.php.net/bug.php?id=49874
209 | ob_start();
210 | fpassthru($this->fp);
211 | $this->outdata .= ob_get_contents();
212 | ob_end_clean();
213 |
214 | rewind($this->fp);
215 | ftruncate($this->fp, 0);
216 | }
217 |
218 | private function ProcessInput($final = false)
219 | {
220 | // Automatically determine the type of data based on the header signature.
221 | if ($this->options["type"] == "auto")
222 | {
223 | if (strlen($this->indata) >= 3)
224 | {
225 | $zlibtest = unpack("n", substr($this->indata, 0, 2));
226 |
227 | if (substr($this->indata, 0, 3) === "\x1F\x8B\x08") $this->options["type"] = "gzip";
228 | else if ((ord($this->indata[0]) & 0x0F) == 8 && ((ord($this->indata[0]) & 0xF0) >> 4) < 8 && $zlibtest[1] % 31 == 0) $this->options["type"] = "zlib";
229 | else $this->options["type"] = "raw";
230 | }
231 | else if ($final) $this->options["type"] = "raw";
232 | }
233 |
234 | if ($this->options["type"] == "gzip")
235 | {
236 | if (!$this->options["header"])
237 | {
238 | if (strlen($this->indata) >= 10)
239 | {
240 | $idcm = substr($this->indata, 0, 3);
241 | $flg = ord($this->indata[3]);
242 |
243 | if ($idcm !== "\x1F\x8B\x08") $this->options["type"] = "ignore";
244 | else
245 | {
246 | // Calculate the number of bytes to skip. If flags are set, the size can be dynamic.
247 | $size = 10;
248 | $y = strlen($this->indata);
249 |
250 | // FLG.FEXTRA
251 | if ($size && ($flg & 0x04))
252 | {
253 | if ($size + 2 >= $y) $size = 0;
254 | else
255 | {
256 | $xlen = unpack("v", substr($this->indata, $size, 2));
257 | $size = ($size + 2 + $xlen <= $y ? $size + 2 + $xlen : 0);
258 | }
259 | }
260 |
261 | // FLG.FNAME
262 | if ($size && ($flg & 0x08))
263 | {
264 | $pos = strpos($this->indata, "\x00", $size);
265 | $size = ($pos !== false ? $pos + 1 : 0);
266 | }
267 |
268 | // FLG.FCOMMENT
269 | if ($size && ($flg & 0x10))
270 | {
271 | $pos = strpos($this->indata, "\x00", $size);
272 | $size = ($pos !== false ? $pos + 1 : 0);
273 | }
274 |
275 | // FLG.FHCRC
276 | if ($size && ($flg & 0x02)) $size = ($size + 2 <= $y ? $size + 2 : 0);
277 |
278 | if ($size)
279 | {
280 | $this->indata = substr($this->indata, $size);
281 | $this->options["header"] = true;
282 | }
283 | }
284 | }
285 | }
286 |
287 | if ($this->options["header"] && strlen($this->indata) > 8)
288 | {
289 | fwrite($this->fp, substr($this->indata, 0, -8));
290 | $this->indata = substr($this->indata, -8);
291 |
292 | $this->ProcessOutput();
293 | }
294 |
295 | if ($final) $this->indata = "";
296 | }
297 | else if ($this->options["type"] == "zlib")
298 | {
299 | if (!$this->options["header"])
300 | {
301 | if (strlen($this->indata) >= 2)
302 | {
303 | $cmf = ord($this->indata[0]);
304 | $flg = ord($this->indata[1]);
305 | $cm = $cmf & 0x0F;
306 | $cinfo = ($cmf & 0xF0) >> 4;
307 |
308 | // Compression method 'deflate' ($cm = 8), window size - 8 ($cinfo < 8), no preset dictionaries ($flg bit 5), checksum validates.
309 | if ($cm != 8 || $cinfo > 7 || ($flg & 0x20) || (($cmf << 8 | $flg) % 31) != 0) $this->options["type"] = "ignore";
310 | else
311 | {
312 | $this->indata = substr($this->indata, 2);
313 | $this->options["header"] = true;
314 | }
315 | }
316 | }
317 |
318 | if ($this->options["header"] && strlen($this->indata) > 4)
319 | {
320 | fwrite($this->fp, substr($this->indata, 0, -4));
321 | $this->indata = substr($this->indata, -4);
322 |
323 | $this->ProcessOutput();
324 | }
325 |
326 | if ($final) $this->indata = "";
327 | }
328 |
329 | if ($this->options["type"] == "raw")
330 | {
331 | fwrite($this->fp, $this->indata);
332 | $this->indata = "";
333 |
334 | $this->ProcessOutput();
335 | }
336 |
337 | // Only set when an unrecoverable header error has occurred for gzip or zlib.
338 | if ($this->options["type"] == "ignore") $this->indata = "";
339 | }
340 |
341 | private function SHL32($num, $bits)
342 | {
343 | if ($bits < 0) $bits = 0;
344 |
345 | return $this->LIM32((int)$num << $bits);
346 | }
347 |
348 | private function LIM32($num)
349 | {
350 | return (int)((int)$num & 0xFFFFFFFF);
351 | }
352 |
353 | private function ADD32($num, $num2)
354 | {
355 | $num = (int)$num;
356 | $num2 = (int)$num2;
357 | $add = ((($num >> 30) & 0x03) + (($num2 >> 30) & 0x03));
358 | $num = ((int)($num & 0x3FFFFFFF) + (int)($num2 & 0x3FFFFFFF));
359 | if ($num & 0x40000000) $add++;
360 | $num = (int)(($num & 0x3FFFFFFF) | (($add & 0x03) << 30));
361 |
362 | return $num;
363 | }
364 | }
365 | ?>
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Cloud Storage Server
2 | ====================
3 |
4 | An open source, extensible, self-hosted cloud storage API. The base server implements a complete file system similar to Amazon Cloud Drive, B2 Cloud Storage, OpenDrive, and other providers. Just don't expect to build a scalable service with this software.
5 |
6 | Cloud Storage Server pairs quite nicely with [Cloud Backup](https://github.com/cubiclesoft/cloud-backup) and [Cloud Storage Tools](https://github.com/cubiclesoft/cloud-storage-tools).
7 |
8 | [](https://cubiclesoft.com/donate/) [](https://cubiclesoft.com/product-support/github/)
9 |
10 | Features
11 | --------
12 |
13 | * Completely self-contained. No need for a separate web server or enterprise database engine - just install PHP and go.
14 | * Works well with [Service Manager](https://github.com/cubiclesoft/service-manager/). Automatically start Cloud Storage Server when the system boots up.
15 | * Command-line driven user management interface. Quickly set up API keys and access levels.
16 | * The /files API implements everything needed in a file-based cloud storage provider: Folder hierarchy management, file upload/download, copy, move, rename, trash and restore, delete, and guest access.
17 | * User initialization for first time use of a specific server extension. Useful for placing a 'welcome message' and/or initial folder setup in their account.
18 | * Extensible. Only limited by your imagination.
19 | * [Remoted API Server](https://github.com/cubiclesoft/remoted-api-server) capable.
20 | * Standards-based, RESTful interface. Also supports most operations over WebSocket.
21 | * Secure. Automatically generates and signs multi-year root and server SSL certificates. You can use other certs if you want OR proxy requests from a properly configured web server.
22 | * Per-user quota management.
23 | * Per-user daily network transfer limits.
24 | * Also has a liberal open source license. MIT or LGPL, your choice.
25 | * Designed for relatively painless integration into your environment.
26 | * Sits on GitHub for all of that pull request and issue tracker goodness to easily submit changes and ideas respectively.
27 |
28 | Uses
29 | ----
30 |
31 | * Build your own private cloud storage solution.
32 | * Use [Cloud Backup](http://cubiclesoft.com/cloud-backup) to send encrypted data to a friend's house who lives in the same town. Rapidly recover lost data in the event of catastropic loss (e.g. fire, flood).
33 | * Backup data from deep behind corporate firewalls via [Remoted API Server](https://github.com/cubiclesoft/remoted-api-server).
34 | * Customized, user-oriented, permission-based APIs.
35 | * See the Nifty Extensions section for more ideas.
36 |
37 | Getting Started
38 | ---------------
39 |
40 | Download or clone the latest software release. If you do not have PHP installed, then download and install the command-line (CLI) version for your OS (e.g. 'apt install php-cli' on Debian/Ubuntu). Windows users try [Portable Apache + PHP](https://github.com/cubiclesoft/portable-apache-maria-db-php-for-windows).
41 |
42 | You'll also need to enable the SQLite and OpenSSL PHP modules for your PHP CLI version (e.g. 'apt install php-sqlite php-openssl' on Debian/Ubuntu, edit the 'php.ini' file on Windows).
43 |
44 | From a command-line, run:
45 |
46 | ```
47 | php install.php
48 | ```
49 |
50 | The installer will ask a series of questions that will create the baseline server configuration. If extensions will be used that require "root" privileges (e.g. /scripts and /feeds), be sure to enter "root" for the user. When adding extensions or upgrading, re-run the installation command before starting the server to avoid problems. Skip the service installation step until you are ready to have the software run at boot.
51 |
52 | Run the user management interface and add a user with access to the 'files' extension (grants access to the /files API):
53 |
54 | ```
55 | php manage.php
56 |
57 | Ready. This is a command-line interface. Enter 'help' to get a list of available commands.
58 |
59 | >adduser yourusername
60 | Host: https://localhost:9892
61 | API key: abcdef12......34567890-1
62 | >adduserext yourusername files
63 | [Files Ext] Allow file download access (Y/N): Y
64 | [Files Ext] Allow folder write, file upload, trash access (Y/N): Y
65 | [Files Ext] Allow permanent folder and file delete access (Y/N): Y
66 | [Files Ext] Allow guest creation/deletion (Y/N): Y
67 | >exit
68 | ```
69 |
70 | Be sure to copy the `Host` and `API key` somewhere. Depending on the configuration and setup, `Host` might not be correct. Adjust it accordingly.
71 |
72 | To make sure the server works correctly, run it directly at first:
73 |
74 | ```
75 | php server.php
76 | ```
77 |
78 | Then connect to the server with a valid client SDK using the `Host` and `API key` from earlier.
79 |
80 | The easiest client to get started with is to use [Cloud Storage Tools](https://github.com/cubiclesoft/cloud-storage-tools).
81 |
82 | Example usage using the included PHP SDK with the /files extension:
83 |
84 | ```php
85 | SetAccessInfo($host, $apikey, false, false);
100 |
101 | // Force-loads the server's SSL cert and assume it to be valid.
102 | $result = $css->GetSSLInfo();
103 | if (!$result["success"])
104 | {
105 | var_dump($result);
106 |
107 | exit();
108 | }
109 |
110 | // Get the server's timestamp and root folder ID.
111 | $result = $css->GetRootFolderID();
112 | if (!$result["success"])
113 | {
114 | var_dump($result);
115 |
116 | exit();
117 | }
118 |
119 | var_dump($result);
120 |
121 | // Obtain an object ID via a path.
122 | $result = $css->GetObjectByPath("/");
123 | if (!$result["success"])
124 | {
125 | var_dump($result);
126 |
127 | exit();
128 | }
129 |
130 | $id = $result["body"]["object"]["id"];
131 |
132 | // Retrieve the folder list associated with an ID.
133 | $result = $css->GetFolderList($id);
134 | if (!$result["success"])
135 | {
136 | var_dump($result);
137 |
138 | exit();
139 | }
140 |
141 | var_dump($result);
142 | ```
143 |
144 | Once everything is good to go, re-run the installer to install the server as a system service:
145 |
146 | ```
147 | php install.php
148 | ```
149 |
150 | Nifty Extensions
151 | ----------------
152 |
153 | Both of the /feeds and /scripts extensions are considered mostly obsolete. If you need them, consider using [xcron](https://github.com/cubiclesoft/xcron) instead, which accomplishes nearly all of the same tasks that /feeds and /scripts does but far more elegantly.
154 |
155 | * [/feeds](https://github.com/cubiclesoft/cloud-storage-server-ext-feeds) - A powerful and flexible API to send and schedule notifications with data payloads attached. The /feeds API allows for scheduling future notifications and has powerful filtering features to only return information that a monitoring application is interested in.
156 | * [/scripts](https://github.com/cubiclesoft/cloud-storage-server-ext-scripts) - A powerful and flexible API to initiate named, long-running scripts as other users on a system (e.g. root/SYSTEM). The /scripts API uses a crontab-like, per-user definition file to define what scripts can be run. Parameter and limited 'stdin' passing support. While scripts are running, track status and/or monitor start and completion of script runs.
157 |
158 | Got an idea for an extension that you would like to see included? Open an issue on the issue tracker. Due to Cloud Storage Server being a server product that is intended to be network-facing, all included extensions must pass rigorous CubicleSoft standards. Extensions must also be dual licensed under MIT/LGPL.
159 |
160 | Extension: /files
161 | ------------------
162 |
163 | The files extension implements the /files/v1 API. To try to keep this page relatively short, here is the list of available APIs, the input request method, and successful return values (always JSON output with the exceptions of 'download' and 'downloaddatabase'):
164 |
165 | GET /files/v1/folder/list/ID
166 |
167 | * ID - Folder ID
168 | * Returns: success (boolean), items (array)
169 |
170 | POST /files/v1/folder/create
171 |
172 | * name - Folder name
173 | * Returns: success (boolean), folder (array)
174 |
175 | GET /files/v1/trash/list
176 |
177 | * Returns: success (boolean), items (array)
178 |
179 | POST /files/v1/file/upload/ID
180 |
181 | * ID - Parent folder ID
182 | * name - Filename
183 | * data - File data
184 | * Returns: success (boolean), id (string)
185 |
186 | GET /files/v1/file/download/ID[/filename]
187 |
188 | * ID - File ID
189 | * filename - Ignored but used by most web browsers
190 | * Returns: Binary data
191 |
192 | GET /files/v1/file/downloaddatabase[/filename]
193 |
194 | * filename - Ignored but used by most web browsers
195 | * Returns: Binary data (the user's entire SQLite files database)
196 |
197 | GET /files/v1/object/bypath/...
198 |
199 | * ... - A human-readable path in the system from the root of the user's access level (i.e. a guest user's root is usually a subset of the entire system)
200 | * Returns: success (boolean), object (array)
201 |
202 | GET /files/v1/object/byname/ID/NAME
203 |
204 | * ID - Parent folder ID
205 | * NAME - A human-readable name or path in the system from ID
206 | * Returns: success (boolean), object (array)
207 |
208 | GET /files/v1/object/byid/ID
209 |
210 | * ID - Object ID
211 | * Returns: success (boolean), object (array)
212 |
213 | POST /files/v1/object/copy
214 |
215 | * srcid - Source object ID
216 | * destid - Destination object ID
217 | * Returns: success (boolean)
218 |
219 | POST /files/v1/object/move
220 |
221 | * srcid - Source object ID
222 | * destfolderid - Destination folder ID
223 | * Returns: success (boolean)
224 |
225 | POST /files/v1/object/rename/ID
226 |
227 | * ID - Object ID
228 | * name - New object name
229 | * Returns: success (boolean), object (array)
230 |
231 | POST /files/v1/object/trash/ID
232 |
233 | * ID - Object ID
234 | * Returns: success (boolean), object (array)
235 |
236 | POST /files/v1/object/restore/ID
237 |
238 | * ID - Object ID
239 | * Returns: success (boolean), object (array)
240 |
241 | POST /files/v1/object/delete/ID
242 |
243 | * ID - Object ID
244 | * Returns: success (boolean)
245 |
246 | GET /files/v1/user/root
247 |
248 | * Returns: success (boolean), id (string), download (boolean), upload (boolean), delete (boolean), guests (boolean), expires (integer)
249 | * Summary: Returns root ID, permissions, and expiration of the guest's API key.
250 |
251 | GET /files/v1/user/limits
252 |
253 | * Returns: success (boolean), info (array)
254 | * Summary: The 'info' array contains: quota, transferlimit, fileuploadlimit, uploadbytesleft, downloadbytesleft.
255 |
256 | GET /files/v1/guest/list
257 |
258 | * Returns: success (boolean), guests (array)
259 |
260 | POST /files/v1/guest/create
261 |
262 | * rootid - Root object ID
263 | * read - Guest can download files
264 | * write - Guest can upload, trash, and restore
265 | * delete - Guest can permanently delete objects
266 | * expires - Unix timestamp (integer)
267 | * Returns: success (boolean), id (string), info (array)
268 | * Summary: The 'info' array contains: apikey, created, expires, info (rootid, read, write, delete)
269 |
270 | POST /files/v1/guest/delete/ID
271 |
272 | * ID - Guest ID
273 | * Returns: success (boolean)
274 |
--------------------------------------------------------------------------------
/support/phpseclib/Crypt/RC4.php:
--------------------------------------------------------------------------------
1 |
20 | * setKey('abcdefgh');
26 | *
27 | * $size = 10 * 1024;
28 | * $plaintext = '';
29 | * for ($i = 0; $i < $size; $i++) {
30 | * $plaintext.= 'a';
31 | * }
32 | *
33 | * echo $rc4->decrypt($rc4->encrypt($plaintext));
34 | * ?>
35 | *
36 | *
37 | * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
38 | * of this software and associated documentation files (the "Software"), to deal
39 | * in the Software without restriction, including without limitation the rights
40 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41 | * copies of the Software, and to permit persons to whom the Software is
42 | * furnished to do so, subject to the following conditions:
43 | *
44 | * The above copyright notice and this permission notice shall be included in
45 | * all copies or substantial portions of the Software.
46 | *
47 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53 | * THE SOFTWARE.
54 | *
55 | * @category Crypt
56 | * @package Crypt_RC4
57 | * @author Jim Wigginton
58 | * @copyright 2007 Jim Wigginton
59 | * @license http://www.opensource.org/licenses/mit-license.html MIT License
60 | * @link http://phpseclib.sourceforge.net
61 | */
62 |
63 | /**
64 | * Include Crypt_Base
65 | *
66 | * Base cipher class
67 | */
68 | if (!class_exists('Crypt_Base')) {
69 | include_once 'Base.php';
70 | }
71 |
72 | /**#@+
73 | * @access private
74 | * @see self::_crypt()
75 | */
76 | define('CRYPT_RC4_ENCRYPT', 0);
77 | define('CRYPT_RC4_DECRYPT', 1);
78 | /**#@-*/
79 |
80 | /**
81 | * Pure-PHP implementation of RC4.
82 | *
83 | * @package Crypt_RC4
84 | * @author Jim Wigginton
85 | * @access public
86 | */
87 | class Crypt_RC4 extends Crypt_Base
88 | {
89 | /**
90 | * Block Length of the cipher
91 | *
92 | * RC4 is a stream cipher
93 | * so we the block_size to 0
94 | *
95 | * @see Crypt_Base::block_size
96 | * @var int
97 | * @access private
98 | */
99 | var $block_size = 0;
100 |
101 | /**
102 | * Key Length (in bytes)
103 | *
104 | * @see Crypt_RC4::setKeyLength()
105 | * @var int
106 | * @access private
107 | */
108 | var $key_length = 128; // = 1024 bits
109 |
110 | /**
111 | * The namespace used by the cipher for its constants.
112 | *
113 | * @see Crypt_Base::const_namespace
114 | * @var string
115 | * @access private
116 | */
117 | var $const_namespace = 'RC4';
118 |
119 | /**
120 | * The mcrypt specific name of the cipher
121 | *
122 | * @see Crypt_Base::cipher_name_mcrypt
123 | * @var string
124 | * @access private
125 | */
126 | var $cipher_name_mcrypt = 'arcfour';
127 |
128 | /**
129 | * Holds whether performance-optimized $inline_crypt() can/should be used.
130 | *
131 | * @see Crypt_Base::inline_crypt
132 | * @var mixed
133 | * @access private
134 | */
135 | var $use_inline_crypt = false; // currently not available
136 |
137 | /**
138 | * The Key
139 | *
140 | * @see self::setKey()
141 | * @var string
142 | * @access private
143 | */
144 | var $key;
145 |
146 | /**
147 | * The Key Stream for decryption and encryption
148 | *
149 | * @see self::setKey()
150 | * @var array
151 | * @access private
152 | */
153 | var $stream;
154 |
155 | /**
156 | * Default Constructor.
157 | *
158 | * Determines whether or not the mcrypt extension should be used.
159 | *
160 | * @see Crypt_Base::Crypt_Base()
161 | * @return Crypt_RC4
162 | * @access public
163 | */
164 | function __construct()
165 | {
166 | parent::__construct(CRYPT_MODE_STREAM);
167 | }
168 |
169 | /**
170 | * PHP4 compatible Default Constructor.
171 | *
172 | * @see self::__construct()
173 | * @access public
174 | */
175 | function Crypt_RC4()
176 | {
177 | $this->__construct();
178 | }
179 |
180 | /**
181 | * Test for engine validity
182 | *
183 | * This is mainly just a wrapper to set things up for Crypt_Base::isValidEngine()
184 | *
185 | * @see Crypt_Base::Crypt_Base()
186 | * @param int $engine
187 | * @access public
188 | * @return bool
189 | */
190 | function isValidEngine($engine)
191 | {
192 | if ($engine == CRYPT_ENGINE_OPENSSL) {
193 | if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
194 | $this->cipher_name_openssl = 'rc4-40';
195 | } else {
196 | switch (strlen($this->key)) {
197 | case 5:
198 | $this->cipher_name_openssl = 'rc4-40';
199 | break;
200 | case 8:
201 | $this->cipher_name_openssl = 'rc4-64';
202 | break;
203 | case 16:
204 | $this->cipher_name_openssl = 'rc4';
205 | break;
206 | default:
207 | return false;
208 | }
209 | }
210 | }
211 |
212 | return parent::isValidEngine($engine);
213 | }
214 |
215 | /**
216 | * Dummy function.
217 | *
218 | * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
219 | * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
220 | * calling setKey().
221 | *
222 | * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
223 | * the IV's are relatively easy to predict, an attack described by
224 | * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
225 | * can be used to quickly guess at the rest of the key. The following links elaborate:
226 | *
227 | * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
228 | * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
229 | *
230 | * @param string $iv
231 | * @see self::setKey()
232 | * @access public
233 | */
234 | function setIV($iv)
235 | {
236 | }
237 |
238 | /**
239 | * Sets the key length
240 | *
241 | * Keys can be between 1 and 256 bytes long.
242 | *
243 | * @access public
244 | * @param int $length
245 | */
246 | function setKeyLength($length)
247 | {
248 | if ($length < 8) {
249 | $this->key_length = 1;
250 | } elseif ($length > 2048) {
251 | $this->key_length = 256;
252 | } else {
253 | $this->key_length = $length >> 3;
254 | }
255 |
256 | parent::setKeyLength($length);
257 | }
258 |
259 | /**
260 | * Encrypts a message.
261 | *
262 | * @see Crypt_Base::decrypt()
263 | * @see self::_crypt()
264 | * @access public
265 | * @param string $plaintext
266 | * @return string $ciphertext
267 | */
268 | function encrypt($plaintext)
269 | {
270 | if ($this->engine != CRYPT_ENGINE_INTERNAL) {
271 | return parent::encrypt($plaintext);
272 | }
273 | return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
274 | }
275 |
276 | /**
277 | * Decrypts a message.
278 | *
279 | * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
280 | * At least if the continuous buffer is disabled.
281 | *
282 | * @see Crypt_Base::encrypt()
283 | * @see self::_crypt()
284 | * @access public
285 | * @param string $ciphertext
286 | * @return string $plaintext
287 | */
288 | function decrypt($ciphertext)
289 | {
290 | if ($this->engine != CRYPT_ENGINE_INTERNAL) {
291 | return parent::decrypt($ciphertext);
292 | }
293 | return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
294 | }
295 |
296 |
297 | /**
298 | * Setup the key (expansion)
299 | *
300 | * @see Crypt_Base::_setupKey()
301 | * @access private
302 | */
303 | function _setupKey()
304 | {
305 | $key = $this->key;
306 | $keyLength = strlen($key);
307 | $keyStream = range(0, 255);
308 | $j = 0;
309 | for ($i = 0; $i < 256; $i++) {
310 | $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
311 | $temp = $keyStream[$i];
312 | $keyStream[$i] = $keyStream[$j];
313 | $keyStream[$j] = $temp;
314 | }
315 |
316 | $this->stream = array();
317 | $this->stream[CRYPT_RC4_DECRYPT] = $this->stream[CRYPT_RC4_ENCRYPT] = array(
318 | 0, // index $i
319 | 0, // index $j
320 | $keyStream
321 | );
322 | }
323 |
324 | /**
325 | * Encrypts or decrypts a message.
326 | *
327 | * @see self::encrypt()
328 | * @see self::decrypt()
329 | * @access private
330 | * @param string $text
331 | * @param int $mode
332 | * @return string $text
333 | */
334 | function _crypt($text, $mode)
335 | {
336 | if ($this->changed) {
337 | $this->_setup();
338 | $this->changed = false;
339 | }
340 |
341 | $stream = &$this->stream[$mode];
342 | if ($this->continuousBuffer) {
343 | $i = &$stream[0];
344 | $j = &$stream[1];
345 | $keyStream = &$stream[2];
346 | } else {
347 | $i = $stream[0];
348 | $j = $stream[1];
349 | $keyStream = $stream[2];
350 | }
351 |
352 | $len = strlen($text);
353 | for ($k = 0; $k < $len; ++$k) {
354 | $i = ($i + 1) & 255;
355 | $ksi = $keyStream[$i];
356 | $j = ($j + $ksi) & 255;
357 | $ksj = $keyStream[$j];
358 |
359 | $keyStream[$i] = $ksj;
360 | $keyStream[$j] = $ksi;
361 | $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]);
362 | }
363 |
364 | return $text;
365 | }
366 | }
367 |
--------------------------------------------------------------------------------
/sdks/php/sdk_cloud_storage_server_api_base.php:
--------------------------------------------------------------------------------
1 | web = new WebBrowser();
19 | $this->fp = false;
20 | $this->host = false;
21 | $this->apikey = false;
22 | $this->cafile = false;
23 | $this->cacert = false;
24 | $this->cert = false;
25 | $this->apiprefix = false;
26 | }
27 |
28 | public function SetAccessInfo($host, $apikey, $cafile, $cert)
29 | {
30 | $this->web = new WebBrowser();
31 | if (is_resource($this->fp)) @fclose($this->fp);
32 | $this->fp = false;
33 | if (substr($host, -1) === "/") $host = substr($host, 0, -1);
34 | $this->host = $host;
35 | $this->apikey = $apikey;
36 | $this->cafile = $cafile;
37 | $this->cert = $cert;
38 | }
39 |
40 | public function GetSSLInfo()
41 | {
42 | if ($this->host === false) return array("success" => false, "error" => self::CSS_Translate("Missing host."), "errorcode" => "no_access_info");
43 |
44 | if ($this->cafile !== false && $this->cacert === false) $this->cacert = @file_get_contents($this->cafile);
45 |
46 | if (substr($this->host, 0, 7) === "http://" || RemotedAPI::IsRemoted($this->host))
47 | {
48 | $this->cacert = "";
49 | $this->cert = "";
50 | }
51 | else if ($this->cacert === false || $this->cert === false)
52 | {
53 | $this->cacert = false;
54 | $this->cert = false;
55 |
56 | $this->neterror = "";
57 |
58 | $options = array(
59 | "peer_cert_callback" => array($this, "Internal_PeerCertificateCheck"),
60 | "peer_cert_callback_opts" => "",
61 | "sslopts" => self::InitSSLOpts(array("verify_peer" => false, "capture_peer_cert_chain" => true))
62 | );
63 |
64 | $result = $this->web->Process($this->host . "/", $options);
65 |
66 | if (!$result["success"])
67 | {
68 | $result["error"] .= " " . $this->neterror;
69 |
70 | return $result;
71 | }
72 | }
73 |
74 | if ($this->cert === false) return array("success" => false, "error" => self::CSS_Translate("Unable to retrieve server certificate."), "errorcode" => "cert_retrieval_failed");
75 | if ($this->cacert === false) return array("success" => false, "error" => self::CSS_Translate("Unable to retrieve server CA certificate."), "errorcode" => "cacert_retrieval_failed");
76 |
77 | return array("success" => true, "cacert" => $this->cacert, "cert" => $this->cert);
78 | }
79 |
80 | public function InitSSLCache($host, $cafile, $certfile)
81 | {
82 | if (!file_exists($cafile) || !file_exists($certfile))
83 | {
84 | $this->SetAccessInfo($host, false, false, false);
85 |
86 | $result = $this->GetSSLInfo();
87 | if (!$result["success"]) return array("success" => false, "error" => "Unable to get SSL information.", "errorcode" => "get_ssl_info_failed", "info" => $result);
88 |
89 | file_put_contents($cafile, $result["cacert"]);
90 | file_put_contents($certfile, $result["cert"]);
91 | }
92 |
93 | return array("success" => true);
94 | }
95 |
96 | protected static function CSS_Translate()
97 | {
98 | $args = func_get_args();
99 | if (!count($args)) return "";
100 |
101 | return call_user_func_array((defined("CS_TRANSLATE_FUNC") && function_exists(CS_TRANSLATE_FUNC) ? CS_TRANSLATE_FUNC : "sprintf"), $args);
102 | }
103 |
104 | // Internal function to retrieve a X509 SSL certificate during the initial connection to confirm that this is the correct target server.
105 | public function Internal_PeerCertificateCheck($type, $cert, $opts)
106 | {
107 | if (is_array($cert))
108 | {
109 | // The server is incorrectly configured if it doesn't have the self-signed root certificate in the chain.
110 | if (count($cert) < 2)
111 | {
112 | $this->neterror = "Certificate chain is missing the root certificate. Remote host is incorrectly configured.";
113 |
114 | return false;
115 | }
116 |
117 | // The last entry is the root cert.
118 | if (!openssl_x509_export($cert[count($cert) - 1], $str))
119 | {
120 | $this->neterror = "Certificate chain contains an invalid root certificate. Corrupted on remote host?";
121 |
122 | return false;
123 | }
124 |
125 | $this->cacert = $str;
126 | }
127 | else
128 | {
129 | if (!openssl_x509_export($cert, $str))
130 | {
131 | $this->neterror = "Server certificate is invalid. Corrupted on remote host?";
132 |
133 | return false;
134 | }
135 |
136 | // Initial setup automatically trusts the SSL/TLS certificate of the host.
137 | if ($this->cert === false) $this->cert = $str;
138 | else if ($str !== $this->cert)
139 | {
140 | $this->neterror = "Certificate does not exactly match local certificate. Your client is either under a MITM attack or the remote host changed certificates.";
141 |
142 | return false;
143 | }
144 | }
145 |
146 | return true;
147 | }
148 |
149 | protected static function InitSSLOpts($options)
150 | {
151 | $result = array_merge(array(
152 | "ciphers" => "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS",
153 | "disable_compression" => true,
154 | "allow_self_signed" => false,
155 | "verify_peer_name" => false,
156 | "verify_depth" => 3,
157 | "capture_peer_cert" => true,
158 | ), $options);
159 |
160 | return $result;
161 | }
162 |
163 | protected function InitWebSocket()
164 | {
165 | if ($this->host === false || $this->apikey === false) return array("success" => false, "error" => self::CSS_Translate("Missing host or API key."), "errorcode" => "no_access_info");
166 | if ($this->cafile === false || $this->cert === false) return array("success" => false, "error" => self::CSS_Translate("Missing SSL Certificate or Certificate Authority filename. Call GetSSLInfo() to initialize for the first time and be sure to save the results."), "errorcode" => "critical_ssl_info_missing");
167 |
168 | $url = $this->host;
169 |
170 | // Handle Remoted API connections.
171 | if ($this->fp === false && RemotedAPI::IsRemoted($this->host))
172 | {
173 | $result = RemotedAPI::Connect($this->host);
174 | if (!$result["success"]) return $result;
175 |
176 | $this->fp = $result["fp"];
177 | $url = $result["url"];
178 |
179 | $options = array(
180 | "fp" => $this->fp,
181 | "headers" => array(
182 | "X-APIKey" => $this->apikey
183 | )
184 | );
185 | }
186 | else
187 | {
188 | $options = array(
189 | "headers" => array(
190 | "X-APIKey" => $this->apikey
191 | ),
192 | "peer_cert_callback" => array($this, "Internal_PeerCertificateCheck"),
193 | "peer_cert_callback_opts" => "",
194 | "sslopts" => self::InitSSLOpts(array("cafile" => $this->cafile, "verify_peer" => true))
195 | );
196 | }
197 |
198 | if (!class_exists("WebSocket", false)) require_once str_replace("\\", "/", dirname(__FILE__)) . "/websocket.php";
199 |
200 | $ws = new WebSocket();
201 |
202 | $wsurl = str_replace(array("https://", "http://"), array("wss://", "ws://"), $url);
203 | $result = $ws->Connect($wsurl . $this->apiprefix, $url, $options);
204 | if (!$result["success"]) return $result;
205 |
206 | $result["ws"] = $ws;
207 |
208 | return $result;
209 | }
210 |
211 | protected function RunAPI($method, $apipath, $options = array(), $expected = 200, $encodejson = true, $decodebody = true)
212 | {
213 | if ($this->host === false || $this->apikey === false) return array("success" => false, "error" => self::CSS_Translate("Missing host or API key."), "errorcode" => "no_access_info");
214 | if ($this->cafile === false || $this->cert === false) return array("success" => false, "error" => self::CSS_Translate("Missing SSL Certificate or Certificate Authority filename. Call GetSSLInfo() to initialize for the first time and be sure to save the results."), "errorcode" => "critical_ssl_info_missing");
215 |
216 | $url = $this->host;
217 |
218 | // Handle Remoted API connections.
219 | if ($this->fp === false && RemotedAPI::IsRemoted($this->host))
220 | {
221 | $result = RemotedAPI::Connect($this->host);
222 | if (!$result["success"]) return $result;
223 |
224 | $this->fp = $result["fp"];
225 | $url = $result["url"];
226 |
227 | $options2 = array(
228 | "fp" => $this->fp,
229 | "method" => $method,
230 | "headers" => array(
231 | "Connection" => "keep-alive",
232 | "X-APIKey" => $this->apikey
233 | )
234 | );
235 | }
236 | else if ($this->fp !== false)
237 | {
238 | $url = RemotedAPI::ExtractRealHost($url);
239 |
240 | $options2 = array(
241 | "fp" => $this->fp,
242 | "method" => $method,
243 | "headers" => array(
244 | "Connection" => "keep-alive"
245 | )
246 | );
247 | }
248 | else
249 | {
250 | $options2 = array(
251 | "method" => $method,
252 | "headers" => array(
253 | "Connection" => "keep-alive",
254 | "X-APIKey" => $this->apikey
255 | ),
256 | "peer_cert_callback" => array($this, "Internal_PeerCertificateCheck"),
257 | "peer_cert_callback_opts" => "",
258 | "sslopts" => self::InitSSLOpts(array("cafile" => $this->cafile, "verify_peer" => true))
259 | );
260 | }
261 |
262 | if ($encodejson && $method !== "GET")
263 | {
264 | $options2["headers"]["Content-Type"] = "application/json";
265 | $options2["body"] = json_encode($options);
266 | }
267 | else
268 | {
269 | $options2 = array_merge($options2, $options);
270 | }
271 |
272 | $result = $this->web->Process($url . $this->apiprefix . "/" . $apipath, $options2);
273 |
274 | if (!$result["success"] && $this->fp !== false)
275 | {
276 | // If the server terminated the connection, then re-establish the connection and rerun the request.
277 | if (is_resource($this->fp)) @fclose($this->fp);
278 | $this->fp = false;
279 |
280 | return $this->RunAPI($method, $apipath, $options, $expected, $encodejson, $decodebody);
281 | }
282 |
283 | if (!$result["success"]) return $result;
284 |
285 | if (isset($result["fp"]) && is_resource($result["fp"])) $this->fp = $result["fp"];
286 | else $this->fp = false;
287 |
288 | // Cloud Storage Server always responds with 400 Bad Request for errors. Attempt to decode the error.
289 | if ($result["response"]["code"] == 400)
290 | {
291 | $error = @json_decode($result["body"], true);
292 | if (is_array($error) && isset($error["success"]) && !$error["success"]) return $error;
293 | }
294 |
295 | if ($result["response"]["code"] != $expected) return array("success" => false, "error" => self::CSS_Translate("Expected a %d response from Cloud Storage Server. Received '%s'.", $expected, $result["response"]["line"]), "errorcode" => "unexpected_cloud_storage_server_response", "info" => $result);
296 |
297 | if ($decodebody) $result["body"] = json_decode($result["body"], true);
298 |
299 | return $result;
300 | }
301 | }
302 | ?>
--------------------------------------------------------------------------------
/sdks/php/random.php:
--------------------------------------------------------------------------------
1 | mode = false;
15 | $this->fp = false;
16 | $this->cryptosafe = $cryptosafe;
17 |
18 | // Native first (PHP 7 and later).
19 | if (function_exists("random_bytes")) $this->mode = "native";
20 |
21 | // OpenSSL fallback.
22 | if ($this->mode === false && function_exists("openssl_random_pseudo_bytes"))
23 | {
24 | // PHP 5.4.0 introduced native Windows CryptGenRandom() integration via php_win32_get_random_bytes() for performance.
25 | @openssl_random_pseudo_bytes(4, $strong);
26 | if ($strong) $this->mode = "openssl";
27 | }
28 |
29 | // Locate a (relatively) suitable source of entropy or raise an exception.
30 | if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN")
31 | {
32 | // PHP 5.3.0 introduced native Windows CryptGenRandom() integration via php_win32_get_random_bytes() for functionality.
33 | if ($this->mode === false && PHP_VERSION_ID > 50300 && function_exists("mcrypt_create_iv")) $this->mode = "mcrypt";
34 | }
35 | else
36 | {
37 | if (!$cryptosafe && $this->mode === false && file_exists("/dev/arandom"))
38 | {
39 | // OpenBSD. mcrypt doesn't attempt to use this despite claims of higher quality entropy with performance.
40 | $this->fp = @fopen("/dev/arandom", "rb");
41 | if ($this->fp !== false) $this->mode = "file";
42 | }
43 |
44 | if ($cryptosafe && $this->mode === false && file_exists("/dev/random"))
45 | {
46 | // Everything else.
47 | $this->fp = @fopen("/dev/random", "rb");
48 | if ($this->fp !== false) $this->mode = "file";
49 | }
50 |
51 | if (!$cryptosafe && $this->mode === false && file_exists("/dev/urandom"))
52 | {
53 | // Everything else.
54 | $this->fp = @fopen("/dev/urandom", "rb");
55 | if ($this->fp !== false) $this->mode = "file";
56 | }
57 |
58 | if ($this->mode === false && function_exists("mcrypt_create_iv"))
59 | {
60 | // mcrypt_create_iv() is last because it opens and closes a file handle every single call.
61 | $this->mode = "mcrypt";
62 | }
63 | }
64 |
65 | // Throw an exception if unable to find a suitable entropy source.
66 | if ($this->mode === false)
67 | {
68 | throw new Exception(self::RNG_Translate("Unable to locate a suitable entropy source."));
69 | exit();
70 | }
71 | }
72 |
73 | public function __destruct()
74 | {
75 | if ($this->mode === "file") fclose($this->fp);
76 | }
77 |
78 | public function GetBytes($length)
79 | {
80 | if ($this->mode === false) return false;
81 |
82 | $length = (int)$length;
83 | if ($length < 1) return false;
84 |
85 | $result = "";
86 | do
87 | {
88 | switch ($this->mode)
89 | {
90 | case "native": $data = @random_bytes($length); break;
91 | case "openssl": $data = @openssl_random_pseudo_bytes($length, $strong); if (!$strong) $data = false; break;
92 | case "mcrypt": $data = @mcrypt_create_iv($length, ($this->cryptosafe ? MCRYPT_DEV_RANDOM : MCRYPT_DEV_URANDOM)); break;
93 | case "file": $data = @fread($this->fp, $length); break;
94 | default: $data = false;
95 | }
96 | if ($data === false) return false;
97 |
98 | $result .= $data;
99 | } while (strlen($result) < $length);
100 |
101 | return substr($result, 0, $length);
102 | }
103 |
104 | public function GenerateToken($length = 64)
105 | {
106 | $data = $this->GetBytes($length);
107 | if ($data === false) return false;
108 |
109 | return bin2hex($data);
110 | }
111 |
112 | // Get a random number between $min and $max (inclusive).
113 | public function GetInt($min, $max)
114 | {
115 | $min = (int)$min;
116 | $max = (int)$max;
117 | if ($max < $min) return false;
118 | if ($min == $max) return $min;
119 |
120 | $range = $max - $min + 1;
121 |
122 | $bits = 1;
123 | while ((1 << $bits) <= $range) $bits++;
124 |
125 | $numbytes = (int)(($bits + 7) / 8);
126 | $mask = (1 << $bits) - 1;
127 |
128 | do
129 | {
130 | $data = $this->GetBytes($numbytes);
131 | if ($data === false) return false;
132 |
133 | $result = 0;
134 | for ($x = 0; $x < $numbytes; $x++)
135 | {
136 | $result = ($result * 256) + ord($data[$x]);
137 | }
138 |
139 | $result = $result & $mask;
140 | } while ($result >= $range);
141 |
142 | return $result + $min;
143 | }
144 |
145 | // Convenience method to generate a random alphanumeric string.
146 | public function GenerateString($size = 32)
147 | {
148 | $result = "";
149 | for ($x = 0; $x < $size; $x++)
150 | {
151 | $data = $this->GetInt(0, 61);
152 | if ($data === false) return false;
153 |
154 | $result .= self::$alphanum[$data];
155 | }
156 |
157 | return $result;
158 | }
159 |
160 | public function GenerateWordLite(&$freqmap, $len)
161 | {
162 | $totalc = 0;
163 | $totalv = 0;
164 | foreach ($freqmap["consonants"] as $chr => $num) $totalc += $num;
165 | foreach ($freqmap["vowels"] as $chr => $num) $totalv += $num;
166 |
167 | if ($totalc <= 0 || $totalv <= 0) return false;
168 |
169 | $result = "";
170 | for ($x = 0; $x < $len; $x++)
171 | {
172 | if ($x % 2)
173 | {
174 | $data = $this->GetInt(0, $totalv - 1);
175 | if ($data === false) return false;
176 |
177 | foreach ($freqmap["vowels"] as $chr => $num)
178 | {
179 | if ($num > $data)
180 | {
181 | $result .= $chr;
182 |
183 | break;
184 | }
185 |
186 | $data -= $num;
187 | }
188 | }
189 | else
190 | {
191 | $data = $this->GetInt(0, $totalc - 1);
192 | if ($data === false) return false;
193 |
194 | foreach ($freqmap["consonants"] as $chr => $num)
195 | {
196 | if ($num > $data)
197 | {
198 | $result .= $chr;
199 |
200 | break;
201 | }
202 |
203 | $data -= $num;
204 | }
205 | }
206 | }
207 |
208 | return $result;
209 | }
210 |
211 | public function GenerateWord(&$freqmap, $len, $separator = "-")
212 | {
213 | $result = "";
214 | $queue = array();
215 | $threshold = $freqmap["threshold"];
216 | $state = "start";
217 | while ($len)
218 | {
219 | //echo $state . " - " . $len . ": " . $result . "\n";
220 | switch ($state)
221 | {
222 | case "start":
223 | {
224 | // The start of the word (or restart).
225 | $path = &$freqmap["start"];
226 | while (count($queue) < $threshold && $len)
227 | {
228 | if ($len > 1 || !$path["*"])
229 | {
230 | // Some part of the word.
231 | $found = false;
232 | if ($path[""])
233 | {
234 | $pos = $this->GetInt(0, $path[""] - 1);
235 |
236 | foreach ($path as $chr => &$info)
237 | {
238 | if (!is_array($info)) continue;
239 |
240 | if ($info["+"] > $pos)
241 | {
242 | $result .= $chr;
243 | $queue[] = $chr;
244 | $path = &$path[$chr];
245 | $len--;
246 |
247 | $found = true;
248 |
249 | break;
250 | }
251 |
252 | $pos -= $info["+"];
253 | }
254 | }
255 |
256 | if (!$found)
257 | {
258 | $state = (count($queue) ? "recovery" : "restart");
259 |
260 | break;
261 | }
262 | }
263 | else
264 | {
265 | // Last letter of the word.
266 | $found = false;
267 | if ($path["*"])
268 | {
269 | $pos = $this->GetInt(0, $path["*"] - 1);
270 |
271 | foreach ($path as $chr => &$info)
272 | {
273 | if (!is_array($info)) continue;
274 |
275 | if ($info["-"] > $pos)
276 | {
277 | $result .= $chr;
278 | $queue[] = $chr;
279 | $path = &$path[$chr];
280 | $len--;
281 |
282 | $found = true;
283 |
284 | break;
285 | }
286 |
287 | $pos -= $info["-"];
288 | }
289 | }
290 |
291 | if (!$found)
292 | {
293 | $state = (count($queue) ? "end" : "restart");
294 |
295 | break;
296 | }
297 | }
298 | }
299 |
300 | if (count($queue) >= $threshold) $state = ($len >= $threshold ? "middle" : "end");
301 |
302 | break;
303 | }
304 | case "middle":
305 | {
306 | // The middle of the word.
307 | $str = implode("", $queue);
308 |
309 | if (!isset($freqmap["middle"][$str])) $state = "recovery";
310 | else
311 | {
312 | $found = false;
313 |
314 | if ($freqmap["middle"][$str][""])
315 | {
316 | $pos = $this->GetInt(0, $freqmap["middle"][$str][""] - 1);
317 |
318 | foreach ($freqmap["middle"][$str] as $chr => $num)
319 | {
320 | if ($chr === "") continue;
321 |
322 | if ($num > $pos)
323 | {
324 | $result .= $chr;
325 | $queue[] = $chr;
326 | array_shift($queue);
327 | $len--;
328 |
329 | if ($len < $threshold) $state = "end";
330 |
331 | $found = true;
332 |
333 | break;
334 | }
335 |
336 | $pos -= $num;
337 | }
338 | }
339 |
340 | if (!$found) $state = "recovery";
341 | }
342 |
343 | break;
344 | }
345 | case "end":
346 | {
347 | if (!isset($freqmap["end"][$len]) || !count($queue) || !isset($freqmap["end"][$len][$queue[count($queue) - 1]])) $state = "restart";
348 | else
349 | {
350 | $path = &$freqmap["end"][$len][$queue[count($queue) - 1]];
351 |
352 | $found = false;
353 |
354 | if ($path[""])
355 | {
356 | $pos = $this->GetInt(0, $path[""] - 1);
357 |
358 | foreach ($path as $str => $num)
359 | {
360 | if ($str === "") continue;
361 |
362 | if ($num > $pos)
363 | {
364 | $result .= $str;
365 | $len = 0;
366 |
367 | $found = true;
368 |
369 | break;
370 | }
371 |
372 | $pos -= $num;
373 | }
374 | }
375 |
376 | if (!$found) $state = "restart";
377 | }
378 |
379 | break;
380 | }
381 | case "recovery":
382 | {
383 | if (!count($queue) || !isset($freqmap["recovery"][$queue[count($queue) - 1]])) $state = "restart";
384 | else
385 | {
386 | $path = &$freqmap["recovery"][$queue[count($queue) - 1]];
387 |
388 | $found = false;
389 |
390 | if ($path[""])
391 | {
392 | $pos = $this->GetInt(0, $path[""] - 1);
393 |
394 | foreach ($path as $chr => $num)
395 | {
396 | if ($chr === "") continue;
397 |
398 | if ($num > $pos)
399 | {
400 | $result .= $chr;
401 | $queue[] = $chr;
402 | array_shift($queue);
403 | $len--;
404 |
405 | $state = ($len >= $threshold ? "middle" : "end");
406 |
407 | $found = true;
408 |
409 | break;
410 | }
411 |
412 | $pos -= $num;
413 | }
414 | }
415 |
416 | if (!$found) $state = "restart";
417 | }
418 |
419 | break;
420 | }
421 | case "restart":
422 | {
423 | $result .= $separator;
424 | $queue = array();
425 | $len -= strlen($separator);
426 |
427 | $state = "start";
428 |
429 | break;
430 | }
431 | }
432 | }
433 |
434 | return $result;
435 | }
436 |
437 | public function GetMode()
438 | {
439 | return $this->mode;
440 | }
441 |
442 | protected static function RNG_Translate()
443 | {
444 | $args = func_get_args();
445 | if (!count($args)) return "";
446 |
447 | return call_user_func_array((defined("CS_TRANSLATE_FUNC") && function_exists(CS_TRANSLATE_FUNC) ? CS_TRANSLATE_FUNC : "sprintf"), $args);
448 | }
449 | }
450 | ?>
--------------------------------------------------------------------------------
/support/random.php:
--------------------------------------------------------------------------------
1 | mode = false;
15 | $this->fp = false;
16 | $this->cryptosafe = $cryptosafe;
17 |
18 | // Native first (PHP 7 and later).
19 | if (function_exists("random_bytes")) $this->mode = "native";
20 |
21 | // OpenSSL fallback.
22 | if ($this->mode === false && function_exists("openssl_random_pseudo_bytes"))
23 | {
24 | // PHP 5.4.0 introduced native Windows CryptGenRandom() integration via php_win32_get_random_bytes() for performance.
25 | @openssl_random_pseudo_bytes(4, $strong);
26 | if ($strong) $this->mode = "openssl";
27 | }
28 |
29 | // Locate a (relatively) suitable source of entropy or raise an exception.
30 | if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN")
31 | {
32 | // PHP 5.3.0 introduced native Windows CryptGenRandom() integration via php_win32_get_random_bytes() for functionality.
33 | if ($this->mode === false && PHP_VERSION_ID > 50300 && function_exists("mcrypt_create_iv")) $this->mode = "mcrypt";
34 | }
35 | else
36 | {
37 | if (!$cryptosafe && $this->mode === false && file_exists("/dev/arandom"))
38 | {
39 | // OpenBSD. mcrypt doesn't attempt to use this despite claims of higher quality entropy with performance.
40 | $this->fp = @fopen("/dev/arandom", "rb");
41 | if ($this->fp !== false) $this->mode = "file";
42 | }
43 |
44 | if ($cryptosafe && $this->mode === false && file_exists("/dev/random"))
45 | {
46 | // Everything else.
47 | $this->fp = @fopen("/dev/random", "rb");
48 | if ($this->fp !== false) $this->mode = "file";
49 | }
50 |
51 | if (!$cryptosafe && $this->mode === false && file_exists("/dev/urandom"))
52 | {
53 | // Everything else.
54 | $this->fp = @fopen("/dev/urandom", "rb");
55 | if ($this->fp !== false) $this->mode = "file";
56 | }
57 |
58 | if ($this->mode === false && function_exists("mcrypt_create_iv"))
59 | {
60 | // mcrypt_create_iv() is last because it opens and closes a file handle every single call.
61 | $this->mode = "mcrypt";
62 | }
63 | }
64 |
65 | // Throw an exception if unable to find a suitable entropy source.
66 | if ($this->mode === false)
67 | {
68 | throw new Exception(self::RNG_Translate("Unable to locate a suitable entropy source."));
69 | exit();
70 | }
71 | }
72 |
73 | public function __destruct()
74 | {
75 | if ($this->mode === "file") fclose($this->fp);
76 | }
77 |
78 | public function GetBytes($length)
79 | {
80 | if ($this->mode === false) return false;
81 |
82 | $length = (int)$length;
83 | if ($length < 1) return false;
84 |
85 | $result = "";
86 | do
87 | {
88 | switch ($this->mode)
89 | {
90 | case "native": $data = @random_bytes($length); break;
91 | case "openssl": $data = @openssl_random_pseudo_bytes($length, $strong); if (!$strong) $data = false; break;
92 | case "mcrypt": $data = @mcrypt_create_iv($length, ($this->cryptosafe ? MCRYPT_DEV_RANDOM : MCRYPT_DEV_URANDOM)); break;
93 | case "file": $data = @fread($this->fp, $length); break;
94 | default: $data = false;
95 | }
96 | if ($data === false) return false;
97 |
98 | $result .= $data;
99 | } while (strlen($result) < $length);
100 |
101 | return substr($result, 0, $length);
102 | }
103 |
104 | public function GenerateToken($length = 64)
105 | {
106 | $data = $this->GetBytes($length);
107 | if ($data === false) return false;
108 |
109 | return bin2hex($data);
110 | }
111 |
112 | // Get a random number between $min and $max (inclusive).
113 | public function GetInt($min, $max)
114 | {
115 | $min = (int)$min;
116 | $max = (int)$max;
117 | if ($max < $min) return false;
118 | if ($min == $max) return $min;
119 |
120 | $range = $max - $min + 1;
121 |
122 | $bits = 1;
123 | while ((1 << $bits) <= $range) $bits++;
124 |
125 | $numbytes = (int)(($bits + 7) / 8);
126 | $mask = (1 << $bits) - 1;
127 |
128 | do
129 | {
130 | $data = $this->GetBytes($numbytes);
131 | if ($data === false) return false;
132 |
133 | $result = 0;
134 | for ($x = 0; $x < $numbytes; $x++)
135 | {
136 | $result = ($result * 256) + ord($data[$x]);
137 | }
138 |
139 | $result = $result & $mask;
140 | } while ($result >= $range);
141 |
142 | return $result + $min;
143 | }
144 |
145 | // Convenience method to generate a random alphanumeric string.
146 | public function GenerateString($size = 32)
147 | {
148 | $result = "";
149 | for ($x = 0; $x < $size; $x++)
150 | {
151 | $data = $this->GetInt(0, 61);
152 | if ($data === false) return false;
153 |
154 | $result .= self::$alphanum[$data];
155 | }
156 |
157 | return $result;
158 | }
159 |
160 | public function GenerateWordLite(&$freqmap, $len)
161 | {
162 | $totalc = 0;
163 | $totalv = 0;
164 | foreach ($freqmap["consonants"] as $chr => $num) $totalc += $num;
165 | foreach ($freqmap["vowels"] as $chr => $num) $totalv += $num;
166 |
167 | if ($totalc <= 0 || $totalv <= 0) return false;
168 |
169 | $result = "";
170 | for ($x = 0; $x < $len; $x++)
171 | {
172 | if ($x % 2)
173 | {
174 | $data = $this->GetInt(0, $totalv - 1);
175 | if ($data === false) return false;
176 |
177 | foreach ($freqmap["vowels"] as $chr => $num)
178 | {
179 | if ($num > $data)
180 | {
181 | $result .= $chr;
182 |
183 | break;
184 | }
185 |
186 | $data -= $num;
187 | }
188 | }
189 | else
190 | {
191 | $data = $this->GetInt(0, $totalc - 1);
192 | if ($data === false) return false;
193 |
194 | foreach ($freqmap["consonants"] as $chr => $num)
195 | {
196 | if ($num > $data)
197 | {
198 | $result .= $chr;
199 |
200 | break;
201 | }
202 |
203 | $data -= $num;
204 | }
205 | }
206 | }
207 |
208 | return $result;
209 | }
210 |
211 | public function GenerateWord(&$freqmap, $len, $separator = "-")
212 | {
213 | $result = "";
214 | $queue = array();
215 | $threshold = $freqmap["threshold"];
216 | $state = "start";
217 | while ($len)
218 | {
219 | //echo $state . " - " . $len . ": " . $result . "\n";
220 | switch ($state)
221 | {
222 | case "start":
223 | {
224 | // The start of the word (or restart).
225 | $path = &$freqmap["start"];
226 | while (count($queue) < $threshold && $len)
227 | {
228 | if ($len > 1 || !$path["*"])
229 | {
230 | // Some part of the word.
231 | $found = false;
232 | if ($path[""])
233 | {
234 | $pos = $this->GetInt(0, $path[""] - 1);
235 |
236 | foreach ($path as $chr => &$info)
237 | {
238 | if (!is_array($info)) continue;
239 |
240 | if ($info["+"] > $pos)
241 | {
242 | $result .= $chr;
243 | $queue[] = $chr;
244 | $path = &$path[$chr];
245 | $len--;
246 |
247 | $found = true;
248 |
249 | break;
250 | }
251 |
252 | $pos -= $info["+"];
253 | }
254 | }
255 |
256 | if (!$found)
257 | {
258 | $state = (count($queue) ? "recovery" : "restart");
259 |
260 | break;
261 | }
262 | }
263 | else
264 | {
265 | // Last letter of the word.
266 | $found = false;
267 | if ($path["*"])
268 | {
269 | $pos = $this->GetInt(0, $path["*"] - 1);
270 |
271 | foreach ($path as $chr => &$info)
272 | {
273 | if (!is_array($info)) continue;
274 |
275 | if ($info["-"] > $pos)
276 | {
277 | $result .= $chr;
278 | $queue[] = $chr;
279 | $path = &$path[$chr];
280 | $len--;
281 |
282 | $found = true;
283 |
284 | break;
285 | }
286 |
287 | $pos -= $info["-"];
288 | }
289 | }
290 |
291 | if (!$found)
292 | {
293 | $state = (count($queue) ? "end" : "restart");
294 |
295 | break;
296 | }
297 | }
298 | }
299 |
300 | if (count($queue) >= $threshold) $state = ($len >= $threshold ? "middle" : "end");
301 |
302 | break;
303 | }
304 | case "middle":
305 | {
306 | // The middle of the word.
307 | $str = implode("", $queue);
308 |
309 | if (!isset($freqmap["middle"][$str])) $state = "recovery";
310 | else
311 | {
312 | $found = false;
313 |
314 | if ($freqmap["middle"][$str][""])
315 | {
316 | $pos = $this->GetInt(0, $freqmap["middle"][$str][""] - 1);
317 |
318 | foreach ($freqmap["middle"][$str] as $chr => $num)
319 | {
320 | if ($chr === "") continue;
321 |
322 | if ($num > $pos)
323 | {
324 | $result .= $chr;
325 | $queue[] = $chr;
326 | array_shift($queue);
327 | $len--;
328 |
329 | if ($len < $threshold) $state = "end";
330 |
331 | $found = true;
332 |
333 | break;
334 | }
335 |
336 | $pos -= $num;
337 | }
338 | }
339 |
340 | if (!$found) $state = "recovery";
341 | }
342 |
343 | break;
344 | }
345 | case "end":
346 | {
347 | if (!isset($freqmap["end"][$len]) || !count($queue) || !isset($freqmap["end"][$len][$queue[count($queue) - 1]])) $state = "restart";
348 | else
349 | {
350 | $path = &$freqmap["end"][$len][$queue[count($queue) - 1]];
351 |
352 | $found = false;
353 |
354 | if ($path[""])
355 | {
356 | $pos = $this->GetInt(0, $path[""] - 1);
357 |
358 | foreach ($path as $str => $num)
359 | {
360 | if ($str === "") continue;
361 |
362 | if ($num > $pos)
363 | {
364 | $result .= $str;
365 | $len = 0;
366 |
367 | $found = true;
368 |
369 | break;
370 | }
371 |
372 | $pos -= $num;
373 | }
374 | }
375 |
376 | if (!$found) $state = "restart";
377 | }
378 |
379 | break;
380 | }
381 | case "recovery":
382 | {
383 | if (!count($queue) || !isset($freqmap["recovery"][$queue[count($queue) - 1]])) $state = "restart";
384 | else
385 | {
386 | $path = &$freqmap["recovery"][$queue[count($queue) - 1]];
387 |
388 | $found = false;
389 |
390 | if ($path[""])
391 | {
392 | $pos = $this->GetInt(0, $path[""] - 1);
393 |
394 | foreach ($path as $chr => $num)
395 | {
396 | if ($chr === "") continue;
397 |
398 | if ($num > $pos)
399 | {
400 | $result .= $chr;
401 | $queue[] = $chr;
402 | array_shift($queue);
403 | $len--;
404 |
405 | $state = ($len >= $threshold ? "middle" : "end");
406 |
407 | $found = true;
408 |
409 | break;
410 | }
411 |
412 | $pos -= $num;
413 | }
414 | }
415 |
416 | if (!$found) $state = "restart";
417 | }
418 |
419 | break;
420 | }
421 | case "restart":
422 | {
423 | $result .= $separator;
424 | $queue = array();
425 | $len -= strlen($separator);
426 |
427 | $state = "start";
428 |
429 | break;
430 | }
431 | }
432 | }
433 |
434 | return $result;
435 | }
436 |
437 | public function GetMode()
438 | {
439 | return $this->mode;
440 | }
441 |
442 | protected static function RNG_Translate()
443 | {
444 | $args = func_get_args();
445 | if (!count($args)) return "";
446 |
447 | return call_user_func_array((defined("CS_TRANSLATE_FUNC") && function_exists(CS_TRANSLATE_FUNC) ? CS_TRANSLATE_FUNC : "sprintf"), $args);
448 | }
449 | }
450 | ?>
--------------------------------------------------------------------------------
/support/cli.php:
--------------------------------------------------------------------------------
1 | $val)
15 | {
16 | if (!isset($options["rules"][$val])) unset($options["shortmap"][$key]);
17 | }
18 | foreach ($options["rules"] as $key => $val)
19 | {
20 | if (!isset($val["arg"])) $options["rules"][$key]["arg"] = false;
21 | if (!isset($val["multiple"])) $options["rules"][$key]["multiple"] = false;
22 | }
23 |
24 | if ($args === false) $args = $_SERVER["argv"];
25 | else if (is_string($args))
26 | {
27 | $args2 = $args;
28 | $args = array();
29 | $inside = false;
30 | $currarg = "";
31 | $y = strlen($args2);
32 | for ($x = 0; $x < $y; $x++)
33 | {
34 | $currchr = substr($args2, $x, 1);
35 |
36 | if ($inside === false && $currchr == " " && $currarg != "")
37 | {
38 | $args[] = $currarg;
39 | $currarg = "";
40 | }
41 | else if ($currchr == "\"" || $currchr == "'")
42 | {
43 | if ($inside === false) $inside = $currchr;
44 | else if ($inside === $currchr) $inside = false;
45 | else $currarg .= $currchr;
46 | }
47 | else if ($currchr == "\\" && $x < $y - 1)
48 | {
49 | $x++;
50 | $currarg .= substr($args2, $x, 1);
51 | }
52 | else if ($inside !== false || $currchr != " ")
53 | {
54 | $currarg .= $currchr;
55 | }
56 | }
57 |
58 | if ($currarg != "") $args[] = $currarg;
59 | }
60 |
61 | $result = array("success" => true, "file" => array_shift($args), "opts" => array(), "params" => array());
62 |
63 | // Look over shortmap to determine if options exist that are one byte (flags) and don't have arguments.
64 | $chrs = array();
65 | foreach ($options["shortmap"] as $key => $val)
66 | {
67 | if (isset($options["rules"][$val]) && !$options["rules"][$val]["arg"]) $chrs[$key] = true;
68 | }
69 |
70 | $allowopt = true;
71 | $y = count($args);
72 | for ($x = 0; $x < $y; $x++)
73 | {
74 | $arg = $args[$x];
75 |
76 | // Attempt to process an option.
77 | $opt = false;
78 | $optval = false;
79 | if ($allowopt && substr($arg, 0, 1) == "-")
80 | {
81 | $pos = strpos($arg, "=");
82 | if ($pos === false) $pos = strlen($arg);
83 | else $optval = substr($arg, $pos + 1);
84 | $arg2 = substr($arg, 1, $pos - 1);
85 |
86 | if (isset($options["rules"][$arg2])) $opt = $arg2;
87 | else if (isset($options["shortmap"][$arg2])) $opt = $options["shortmap"][$arg2];
88 | else if ($x == 0)
89 | {
90 | // Attempt to process as a set of flags.
91 | $y2 = strlen($arg2);
92 | if ($y2 > 0)
93 | {
94 | for ($x2 = 0; $x2 < $y2; $x2++)
95 | {
96 | $currchr = substr($arg2, $x2, 1);
97 |
98 | if (!isset($chrs[$currchr])) break;
99 | }
100 |
101 | if ($x2 == $y2)
102 | {
103 | for ($x2 = 0; $x2 < $y2; $x2++)
104 | {
105 | $opt = $options["shortmap"][substr($arg2, $x2, 1)];
106 |
107 | if (!$options["rules"][$opt]["multiple"]) $result["opts"][$opt] = true;
108 | else
109 | {
110 | if (!isset($result["opts"][$opt])) $result["opts"][$opt] = 0;
111 | $result["opts"][$opt]++;
112 | }
113 | }
114 |
115 | continue;
116 | }
117 | }
118 | }
119 | }
120 |
121 | if ($opt === false)
122 | {
123 | // Is a parameter.
124 | if (substr($arg, 0, 1) === "\"" || substr($arg, 0, 1) === "'") $arg = substr($arg, 1);
125 | if (substr($arg, -1) === "\"" || substr($arg, -1) === "'") $arg = substr($arg, 0, -1);
126 |
127 | $result["params"][] = $arg;
128 |
129 | if (!$options["allow_opts_after_param"]) $allowopt = false;
130 | }
131 | else if (!$options["rules"][$opt]["arg"])
132 | {
133 | // Is a flag by itself.
134 | if (!$options["rules"][$opt]["multiple"]) $result["opts"][$opt] = true;
135 | else
136 | {
137 | if (!isset($result["opts"][$opt])) $result["opts"][$opt] = 0;
138 | $result["opts"][$opt]++;
139 | }
140 | }
141 | else
142 | {
143 | // Is an option.
144 | if ($optval === false)
145 | {
146 | $x++;
147 | if ($x == $y) break;
148 | $optval = $args[$x];
149 | }
150 |
151 | if (substr($optval, 0, 1) === "\"" || substr($optval, 0, 1) === "'") $optval = substr($optval, 1);
152 | if (substr($optval, -1) === "\"" || substr($optval, -1) === "'") $optval = substr($optval, 0, -1);
153 |
154 | if (!$options["rules"][$opt]["multiple"]) $result["opts"][$opt] = $optval;
155 | else
156 | {
157 | if (!isset($result["opts"][$opt])) $result["opts"][$opt] = array();
158 | $result["opts"][$opt][] = $optval;
159 | }
160 | }
161 | }
162 |
163 | return $result;
164 | }
165 |
166 | public static function CanGetUserInputWithArgs(&$args, $prefix)
167 | {
168 | return (($prefix !== false && isset($args["opts"][$prefix]) && is_array($args["opts"][$prefix]) && count($args["opts"][$prefix])) || count($args["params"]));
169 | }
170 |
171 | // Gets a line of input from the user. If the user supplies all information via the command-line, this could be entirely automated.
172 | public static function GetUserInputWithArgs(&$args, $prefix, $question, $default, $noparamsoutput = "", $suppressoutput = false, $callback = false, $callbackopts = false)
173 | {
174 | if (!self::CanGetUserInputWithArgs($args, $prefix) && $noparamsoutput != "")
175 | {
176 | echo "\n" . rtrim($noparamsoutput) . "\n\n";
177 |
178 | $suppressoutput = false;
179 | $noparamsoutput = "";
180 | }
181 |
182 | do
183 | {
184 | $prompt = ($suppressoutput ? "" : $question . ($default !== false ? " [" . $default . "]" : "") . ": ");
185 |
186 | if ($prefix !== false && isset($args["opts"][$prefix]) && is_array($args["opts"][$prefix]) && count($args["opts"][$prefix]))
187 | {
188 | $line = array_shift($args["opts"][$prefix]);
189 | if ($line === "") $line = $default;
190 | if (!$suppressoutput) echo $prompt . $line . "\n";
191 | }
192 | else if (count($args["params"]))
193 | {
194 | $line = array_shift($args["params"]);
195 | if ($line === "") $line = $default;
196 | if (!$suppressoutput) echo $prompt . $line . "\n";
197 | }
198 | else if (strtoupper(substr(php_uname("s"), 0, 3)) != "WIN" && function_exists("readline") && function_exists("readline_add_history"))
199 | {
200 | $line = readline($prompt);
201 | if ($line === false) exit();
202 |
203 | $line = trim($line);
204 | if ($line === "") $line = $default;
205 | if ($line !== false && $line !== "") readline_add_history($line);
206 | }
207 | else
208 | {
209 | echo $prompt;
210 | fflush(STDOUT);
211 | $line = fgets(STDIN);
212 | if ($line === false || ($line === "" && feof(STDIN))) exit();
213 |
214 | $line = trim($line);
215 | if ($line === "") $line = $default;
216 | }
217 |
218 | if ($line === false || (is_callable($callback) && !call_user_func_array($callback, array($line, &$callbackopts))))
219 | {
220 | if ($line !== false) $line = false;
221 | else echo "Please enter a value.\n";
222 |
223 | if (!self::CanGetUserInputWithArgs($args, $prefix) && $noparamsoutput != "")
224 | {
225 | echo "\n" . $noparamsoutput . "\n";
226 |
227 | $noparamsoutput = "";
228 | }
229 |
230 | $suppressoutput = false;
231 | }
232 | } while ($line === false);
233 |
234 | return $line;
235 | }
236 |
237 | // Obtains a valid line of input.
238 | public static function GetLimitedUserInputWithArgs(&$args, $prefix, $question, $default, $allowedoptionsprefix, $allowedoptions, $loop = true, $suppressoutput = false, $multipleuntil = false)
239 | {
240 | $noparamsoutput = $allowedoptionsprefix . "\n\n";
241 | $size = 0;
242 | foreach ($allowedoptions as $key => $val)
243 | {
244 | if ($size < strlen($key)) $size = strlen($key);
245 | }
246 |
247 | foreach ($allowedoptions as $key => $val)
248 | {
249 | $newtab = str_repeat(" ", 2 + $size + 3);
250 | $noparamsoutput .= " " . $key . ":" . str_repeat(" ", $size - strlen($key)) . " " . str_replace("\n\t", "\n" . $newtab, $val) . "\n";
251 | }
252 |
253 | $noparamsoutput .= "\n";
254 |
255 | if ($default === false && count($allowedoptions) == 1)
256 | {
257 | reset($allowedoptions);
258 | $default = key($allowedoptions);
259 | }
260 |
261 | $results = array();
262 | do
263 | {
264 | $displayed = (!count($args["params"]));
265 | $result = self::GetUserInputWithArgs($args, $prefix, $question, $default, $noparamsoutput, $suppressoutput);
266 | if (is_array($multipleuntil) && $multipleuntil["exit"] === $result) break;
267 | $result2 = false;
268 | if (!count($allowedoptions)) break;
269 | foreach ($allowedoptions as $key => $val)
270 | {
271 | if (!strcasecmp($key, $result) || !strcasecmp($val, $result)) $result2 = $key;
272 | }
273 | if ($loop)
274 | {
275 | if ($result2 === false)
276 | {
277 | echo "Please select an option from the list.\n";
278 |
279 | $suppressoutput = false;
280 | }
281 | else if (is_array($multipleuntil))
282 | {
283 | $results[$result2] = $result2;
284 |
285 | $question = $multipleuntil["nextquestion"];
286 | $default = $multipleuntil["nextdefault"];
287 | }
288 | }
289 |
290 | if ($displayed) $noparamsoutput = "";
291 | } while ($loop && ($result2 === false || is_array($multipleuntil)));
292 |
293 | return (is_array($multipleuntil) ? $results : $result2);
294 | }
295 |
296 | // Obtains Yes/No style input.
297 | public static function GetYesNoUserInputWithArgs(&$args, $prefix, $question, $default, $noparamsoutput = "", $suppressoutput = false)
298 | {
299 | $default = (substr(strtoupper(trim($default)), 0, 1) === "Y" ? "Y" : "N");
300 |
301 | $result = self::GetUserInputWithArgs($args, $prefix, $question, $default, $noparamsoutput, $suppressoutput);
302 | $result = (substr(strtoupper(trim($result)), 0, 1) === "Y");
303 |
304 | return $result;
305 | }
306 |
307 | public static function GetHexDump($data)
308 | {
309 | $result = "";
310 |
311 | $x = 0;
312 | $y = strlen($data);
313 | if ($y <= 256) $padwidth = 2;
314 | else if ($y <= 65536) $padwidth = 4;
315 | else if ($y <= 16777216) $padwidth = 6;
316 | else $padwidth = 8;
317 |
318 | $pad = str_repeat(" ", $padwidth);
319 |
320 | $data2 = str_split(strtoupper(bin2hex($data)), 32);
321 | foreach ($data2 as $line)
322 | {
323 | $result .= sprintf("%0" . $padwidth . "X", $x) . " | ";
324 |
325 | $line = str_split($line, 2);
326 | array_splice($line, 8, 0, "");
327 | $result .= implode(" ", $line) . "\n";
328 |
329 | $result .= $pad . " |";
330 | $y2 = $x + 16;
331 | for ($x2 = 0; $x2 < 16 && $x < $y; $x2++)
332 | {
333 | $result .= " ";
334 | if ($x2 === 8) $result .= " ";
335 |
336 | $tempchr = ord($data[$x]);
337 | if ($tempchr === 0x09) $result .= "\\t";
338 | else if ($tempchr === 0x0D) $result .= "\\r";
339 | else if ($tempchr === 0x0A) $result .= "\\n";
340 | else if ($tempchr === 0x00) $result .= "\\0";
341 | else if ($tempchr < 32 || $tempchr > 126) $result .= " ";
342 | else $result .= " " . $data[$x];
343 |
344 | $x++;
345 | }
346 |
347 | $result .= "\n";
348 | }
349 |
350 | return $result;
351 | }
352 |
353 | // Outputs a JSON array (useful for captured output).
354 | public static function DisplayResult($result, $exit = true)
355 | {
356 | if (is_array($result)) echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
357 | else echo $result . "\n";
358 |
359 | if ($exit) exit();
360 | }
361 |
362 | // Useful for reparsing remaining parameters as new arguments.
363 | public static function ReinitArgs(&$args, $newargs)
364 | {
365 | // Process the parameters.
366 | $options = array(
367 | "shortmap" => array(
368 | "?" => "help"
369 | ),
370 | "rules" => array(
371 | )
372 | );
373 |
374 | foreach ($newargs as $arg) $options["rules"][$arg] = array("arg" => true, "multiple" => true);
375 | $options["rules"]["help"] = array("arg" => false);
376 |
377 | $args = self::ParseCommandLine($options, array_merge(array(""), $args["params"]));
378 |
379 | if (isset($args["opts"]["help"])) self::DisplayResult(array("success" => true, "options" => array_keys($options["rules"])));
380 | }
381 |
382 | // Tracks messages for a command-line interface app.
383 | private static $messages = array();
384 |
385 | public static function LogMessage($msg, $data = null)
386 | {
387 | if (isset($data)) $msg .= "\n\t" . trim(str_replace("\n", "\n\t", json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)));
388 |
389 | self::$messages[] = $msg;
390 |
391 | fwrite(STDERR, $msg . "\n");
392 | }
393 |
394 | public static function DisplayError($msg, $result = false, $exit = true)
395 | {
396 | self::LogMessage(($exit ? "[Error] " : "") . $msg);
397 |
398 | if ($result !== false && is_array($result) && isset($result["error"]) && isset($result["errorcode"])) self::LogMessage("[Error] " . $result["error"] . " (" . $result["errorcode"] . ")", (isset($result["info"]) ? $result["info"] : null));
399 |
400 | if ($exit) exit();
401 | }
402 |
403 | public static function GetLogMessages($filters = array())
404 | {
405 | if (is_string($filters)) $filters = array($filters);
406 |
407 | $result = array();
408 | foreach (self::$messages as $message)
409 | {
410 | $found = (!count($filters));
411 | foreach ($filters as $filter)
412 | {
413 | if (preg_match($filter, $message)) $found = true;
414 | }
415 |
416 | if ($found) $result[] = $message;
417 | }
418 |
419 | return $result;
420 | }
421 |
422 | public static function ResetLogMessages()
423 | {
424 | self::$messages = array();
425 | }
426 |
427 |
428 | private static $timerinfo = array();
429 |
430 | public static function StartTimer()
431 | {
432 | $ts = microtime(true);
433 |
434 | self::$timerinfo = array(
435 | "start" => $ts,
436 | "diff" => $ts
437 | );
438 | }
439 |
440 | public static function UpdateTimer()
441 | {
442 | $ts = microtime(true);
443 | $diff = $ts - self::$timerinfo["diff"];
444 | self::$timerinfo["diff"] = $ts;
445 |
446 | $result = array(
447 | "success" => true,
448 | "diff" => sprintf("%.2f", $diff),
449 | "total" => sprintf("%.2f", $ts - self::$timerinfo["start"])
450 | );
451 |
452 | return $result;
453 | }
454 | }
455 | ?>
--------------------------------------------------------------------------------
/support/utf_utils.php:
--------------------------------------------------------------------------------
1 | = 0x0300 && $val <= 0x036F) || ($val >= 0x1DC0 && $val <= 0x1DFF) || ($val >= 0x20D0 && $val <= 0x20FF) || ($val >= 0xFE20 && $val <= 0xFE2F));
21 | }
22 |
23 | public static function Convert($data, $srctype, $desttype)
24 | {
25 | $arr = is_array($data);
26 | if ($arr) $srctype = self::UTF32_ARRAY;
27 | $x = 0;
28 | $y = ($arr ? count($data) : strlen($data));
29 | $result = ($desttype === self::UTF32_ARRAY ? array() : "");
30 | if (!$arr && $srctype === self::UTF32_ARRAY) return $result;
31 |
32 | $first = true;
33 |
34 | if ($srctype === self::UTF8_BOM)
35 | {
36 | if (substr($data, 0, 3) === "\xEF\xBB\xBF") $x = 3;
37 |
38 | $srctype = self::UTF8;
39 | }
40 |
41 | if ($srctype === self::UTF16_BOM)
42 | {
43 | if (substr($data, 0, 2) === "\xFE\xFF")
44 | {
45 | $srctype = self::UTF16_BE;
46 | $x = 2;
47 | }
48 | else if (substr($data, 0, 2) === "\xFF\xFE")
49 | {
50 | $srctype = self::UTF16_LE;
51 | $x = 2;
52 | }
53 | else
54 | {
55 | $srctype = self::UTF16_LE;
56 | }
57 | }
58 |
59 | if ($srctype === self::UTF32_BOM)
60 | {
61 | if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
62 | {
63 | $srctype = self::UTF32_BE;
64 | $x = 4;
65 | }
66 | else if (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
67 | {
68 | $srctype = self::UTF32_LE;
69 | $x = 4;
70 | }
71 | else
72 | {
73 | $srctype = self::UTF32_LE;
74 | }
75 | }
76 |
77 | while ($x < $y)
78 | {
79 | // Read the next valid code point.
80 | $val = false;
81 |
82 | switch ($srctype)
83 | {
84 | case self::UTF8:
85 | {
86 | $tempchr = ord($data[$x]);
87 | if ($tempchr <= 0x7F)
88 | {
89 | $val = $tempchr;
90 | $x++;
91 | }
92 | else if ($tempchr < 0xC2) $x++;
93 | else
94 | {
95 | $left = $y - $x;
96 | if ($left < 2) $x++;
97 | else
98 | {
99 | $tempchr2 = ord($data[$x + 1]);
100 |
101 | if (($tempchr >= 0xC2 && $tempchr <= 0xDF) && ($tempchr2 >= 0x80 && $tempchr2 <= 0xBF))
102 | {
103 | $val = (($tempchr & 0x1F) << 6) | ($tempchr2 & 0x3F);
104 | $x += 2;
105 | }
106 | else if ($left < 3) $x++;
107 | else
108 | {
109 | $tempchr3 = ord($data[$x + 2]);
110 |
111 | if ($tempchr3 < 0x80 || $tempchr3 > 0xBF) $x++;
112 | else
113 | {
114 | if (($tempchr == 0xE0 && ($tempchr2 >= 0xA0 && $tempchr2 <= 0xBF)) || ((($tempchr >= 0xE1 && $tempchr <= 0xEC) || $tempchr == 0xEE || $tempchr == 0xEF) && ($tempchr2 >= 0x80 && $tempchr2 <= 0xBF)) || ($tempchr == 0xED && ($tempchr2 >= 0x80 && $tempchr2 <= 0x9F)))
115 | {
116 | $val = (($tempchr & 0x0F) << 12) | (($tempchr2 & 0x3F) << 6) | ($tempchr3 & 0x3F);
117 | $x += 3;
118 | }
119 | else if ($left < 4) $x++;
120 | else
121 | {
122 | $tempchr4 = ord($data[$x + 3]);
123 |
124 | if ($tempchr4 < 0x80 || $tempchr4 > 0xBF) $x++;
125 | else if (($tempchr == 0xF0 && ($tempchr2 >= 0x90 && $tempchr2 <= 0xBF)) || (($tempchr >= 0xF1 && $tempchr <= 0xF3) && ($tempchr2 >= 0x80 && $tempchr2 <= 0xBF)) || ($tempchr == 0xF4 && ($tempchr2 >= 0x80 && $tempchr2 <= 0x8F)))
126 | {
127 | $val = (($tempchr & 0x07) << 18) | (($tempchr2 & 0x3F) << 12) | (($tempchr3 & 0x3F) << 6) | ($tempchr4 & 0x3F);
128 | $x += 4;
129 | }
130 | else
131 | {
132 | $x++;
133 | }
134 | }
135 | }
136 | }
137 | }
138 | }
139 |
140 | break;
141 | }
142 | case self::UTF16_LE:
143 | {
144 | if ($x + 1 >= $y) $x = $y;
145 | else
146 | {
147 | $val = unpack("v", substr($data, $x, 2))[1];
148 | $x += 2;
149 |
150 | if ($val >= 0xD800 && $val <= 0xDBFF)
151 | {
152 | if ($x + 1 >= $y)
153 | {
154 | $x = $y;
155 | $val = false;
156 | }
157 | else
158 | {
159 | $val2 = unpack("v", substr($data, $x, 2))[1];
160 |
161 | if ($val2 < 0xDC00 || $val2 > 0xDFFF) $val = false;
162 | else
163 | {
164 | $val = ((($val - 0xD800) << 10) | ($val2 - 0xDC00)) + 0x10000;
165 | $x += 2;
166 | }
167 | }
168 | }
169 | }
170 |
171 | break;
172 | }
173 | case self::UTF16_BE:
174 | {
175 | if ($x + 1 >= $y) $x = $y;
176 | else
177 | {
178 | $val = unpack("n", substr($data, $x, 2))[1];
179 | $x += 2;
180 |
181 | if ($val >= 0xD800 && $val <= 0xDBFF)
182 | {
183 | if ($x + 1 >= $y)
184 | {
185 | $x = $y;
186 | $val = false;
187 | }
188 | else
189 | {
190 | $val2 = unpack("n", substr($data, $x, 2))[1];
191 |
192 | if ($val2 < 0xDC00 || $val2 > 0xDFFF) $val = false;
193 | else
194 | {
195 | $val = ((($val - 0xD800) << 10) | ($val2 - 0xDC00)) + 0x10000;
196 | $x += 2;
197 | }
198 | }
199 | }
200 | }
201 |
202 | break;
203 | }
204 | case self::UTF32_LE:
205 | {
206 | if ($x + 3 >= $y) $x = $y;
207 | else
208 | {
209 | $val = unpack("V", substr($data, $x, 4))[1];
210 | $x += 4;
211 | }
212 |
213 | break;
214 | }
215 | case self::UTF32_BE:
216 | {
217 | if ($x + 3 >= $y) $x = $y;
218 | else
219 | {
220 | $val = unpack("N", substr($data, $x, 4))[1];
221 | $x += 4;
222 | }
223 |
224 | break;
225 | }
226 | case self::UTF32_ARRAY:
227 | {
228 | $val = (int)$data[$x];
229 | $x++;
230 |
231 | break;
232 | }
233 | default: $x = $y; break;
234 | }
235 |
236 | // Make sure it is a valid Unicode value.
237 | // 0xD800-0xDFFF are for UTF-16 surrogate pairs. Invalid characters.
238 | // 0xFDD0-0xFDEF are non-characters.
239 | // 0x*FFFE and 0x*FFFF are reserved.
240 | // The largest possible character is 0x10FFFF.
241 | // First character can't be a combining code point.
242 | if ($val !== false && !($val < 0 || ($val >= 0xD800 && $val <= 0xDFFF) || ($val >= 0xFDD0 && $val <= 0xFDEF) || ($val & 0xFFFE) == 0xFFFE || $val > 0x10FFFF || ($first && self::IsCombiningCodePoint($val))))
243 | {
244 | if ($first)
245 | {
246 | if ($desttype === self::UTF8_BOM)
247 | {
248 | $result .= "\xEF\xBB\xBF";
249 |
250 | $desttype = self::UTF8;
251 | }
252 |
253 | if ($desttype === self::UTF16_BOM)
254 | {
255 | $result .= "\xFF\xFE";
256 |
257 | $desttype = self::UTF16_LE;
258 | }
259 |
260 | if ($srctype === self::UTF32_BOM)
261 | {
262 | $result .= "\xFF\xFE\x00\x00";
263 |
264 | $desttype = self::UTF32_LE;
265 | }
266 |
267 | $first = false;
268 | }
269 |
270 | switch ($desttype)
271 | {
272 | case self::UTF8:
273 | {
274 | if ($val <= 0x7F) $result .= chr($val);
275 | else if ($val <= 0x7FF) $result .= chr(0xC0 | ($val >> 6)) . chr(0x80 | ($val & 0x3F));
276 | else if ($val <= 0xFFFF) $result .= chr(0xE0 | ($val >> 12)) . chr(0x80 | (($val >> 6) & 0x3F)) . chr(0x80 | ($val & 0x3F));
277 | else if ($val <= 0x10FFFF) $result .= chr(0xF0 | ($val >> 18)) . chr(0x80 | (($val >> 12) & 0x3F)) . chr(0x80 | (($val >> 6) & 0x3F)) . chr(0x80 | ($val & 0x3F));
278 |
279 | break;
280 | }
281 | case self::UTF16_LE:
282 | {
283 | if ($val <= 0xFFFF) $result .= pack("v", $val);
284 | else
285 | {
286 | $val -= 0x10000;
287 | $result .= pack("v", ((($val >> 10) & 0x3FF) + 0xD800));
288 | $result .= pack("v", (($val & 0x3FF) + 0xDC00));
289 | }
290 |
291 | break;
292 | }
293 | case self::UTF16_BE:
294 | {
295 | if ($val <= 0xFFFF) $result .= pack("n", $val);
296 | else
297 | {
298 | $val -= 0x10000;
299 | $result .= pack("n", ((($val >> 10) & 0x3FF) + 0xD800));
300 | $result .= pack("n", (($val & 0x3FF) + 0xDC00));
301 | }
302 |
303 | break;
304 | }
305 | case self::UTF32_LE:
306 | {
307 | $result .= pack("V", $val);
308 |
309 | break;
310 | }
311 | case self::UTF32_BE:
312 | {
313 | $result .= pack("N", $val);
314 |
315 | break;
316 | }
317 | case self::UTF32_ARRAY:
318 | {
319 | $result[] = $val;
320 |
321 | break;
322 | }
323 | default: $x = $y; break;
324 | }
325 | }
326 | }
327 |
328 | return $result;
329 | }
330 |
331 |
332 | protected const PUNYCODE_BASE = 36;
333 | protected const PUNYCODE_TMIN = 1;
334 | protected const PUNYCODE_TMAX = 26;
335 | protected const PUNYCODE_SKEW = 38;
336 | protected const PUNYCODE_DAMP = 700;
337 | protected const PUNYCODE_INITIAL_BIAS = 72;
338 | protected const PUNYCODE_INITIAL_N = 0x80;
339 | protected const PUNYCODE_DIGIT_MAP = "abcdefghijklmnopqrstuvwxyz0123456789";
340 |
341 | public static function ConvertToPunycode($domain)
342 | {
343 | // Reject invalid domain name lengths.
344 | if (strlen($domain) > 255) return false;
345 |
346 | $parts = explode(".", $domain);
347 |
348 | foreach ($parts as $num => $part)
349 | {
350 | // Reject invalid label lengths.
351 | $y = strlen($part);
352 | if ($y > 63) return false;
353 |
354 | // Skip already encoded portions.
355 | if (substr($part, 0, 4) === "xn--") continue;
356 |
357 | // Convert UTF-8 to UTF-32 code points.
358 | $data = self::Convert($part, self::UTF8, self::UTF32_ARRAY);
359 |
360 | // Handle ASCII code points.
361 | $part2 = "";
362 | foreach ($data as $cp)
363 | {
364 | if ($cp <= 0x7F) $part2 .= strtolower(chr($cp));
365 | }
366 |
367 | $numhandled = strlen($part2);
368 | $y = count($data);
369 |
370 | if ($numhandled >= $y)
371 | {
372 | $parts[$num] = $part2;
373 |
374 | continue;
375 | }
376 |
377 | if ($numhandled) $part2 .= "-";
378 |
379 | $part2 = "xn--" . $part2;
380 |
381 | if (strlen($part2) > 63) return false;
382 |
383 | $bias = self::PUNYCODE_INITIAL_BIAS;
384 | $n = self::PUNYCODE_INITIAL_N;
385 | $delta = 0;
386 | $first = true;
387 |
388 | while ($numhandled < $y)
389 | {
390 | // Find the next largest unhandled code point.
391 | $cp2 = 0x01000000;
392 | foreach ($data as $cp)
393 | {
394 | if ($cp >= $n && $cp2 > $cp) $cp2 = $cp;
395 | }
396 |
397 | // Increase delta but prevent overflow.
398 | $delta += ($cp2 - $n) * ($numhandled + 1);
399 | if ($delta < 0) return false;
400 | $n = $cp2;
401 |
402 | foreach ($data as $cp)
403 | {
404 | if ($cp < $n)
405 | {
406 | $delta++;
407 |
408 | if ($delta < 0) return false;
409 | }
410 | else if ($cp === $n)
411 | {
412 | // Calculate and encode a variable length integer from the delta.
413 | $q = $delta;
414 | $x = 0;
415 | do
416 | {
417 | $x += self::PUNYCODE_BASE;
418 |
419 | if ($x <= $bias) $t = self::PUNYCODE_TMIN;
420 | else if ($x >= $bias + self::PUNYCODE_TMAX) $t = self::PUNYCODE_TMAX;
421 | else $t = $x - $bias;
422 |
423 | if ($q < $t) break;
424 |
425 | $part2 .= self::PUNYCODE_DIGIT_MAP[$t + (($q - $t) % (self::PUNYCODE_BASE - $t))];
426 |
427 | $q = (int)(($q - $t) / (self::PUNYCODE_BASE - $t));
428 |
429 | if (strlen($part2) > 63) return false;
430 | } while (1);
431 |
432 | $part2 .= self::PUNYCODE_DIGIT_MAP[$q];
433 | if (strlen($part2) > 63) return false;
434 |
435 | // Adapt bias.
436 | $numhandled++;
437 | $bias = self::InternalPunycodeAdapt($delta, $numhandled, $first);
438 | $delta = 0;
439 | $first = false;
440 | }
441 | }
442 |
443 | $delta++;
444 | $n++;
445 | }
446 |
447 | $parts[$num] = $part2;
448 | }
449 |
450 | return implode(".", $parts);
451 | }
452 |
453 | public static function ConvertFromPunycode($domain)
454 | {
455 | // Reject invalid domain name lengths.
456 | if (strlen($domain) > 255) return false;
457 |
458 | $parts = explode(".", $domain);
459 |
460 | foreach ($parts as $num => $part)
461 | {
462 | // Reject invalid label lengths.
463 | $y = strlen($part);
464 | if ($y > 63) return false;
465 |
466 | // Skip unencoded portions.
467 | if (substr($part, 0, 4) !== "xn--") continue;
468 |
469 | $part = substr($part, 4);
470 |
471 | // Convert UTF-8 to UTF-32 code points.
472 | $data = self::Convert($part, self::UTF8, self::UTF32_ARRAY);
473 |
474 | // Handle ASCII code points.
475 | $hyphen = ord("-");
476 | for ($x = count($data); $x && $data[$x - 1] !== $hyphen; $x--);
477 | if (!$x) $data2 = array();
478 | else
479 | {
480 | $data2 = array_splice($data, 0, $x - 1);
481 |
482 | array_shift($data);
483 | }
484 |
485 | $numhandled = count($data2);
486 |
487 | $bias = self::PUNYCODE_INITIAL_BIAS;
488 | $n = self::PUNYCODE_INITIAL_N;
489 | $delta = 0;
490 | $first = true;
491 |
492 | $pos = 0;
493 | $y = count($data);
494 | while ($pos < $y)
495 | {
496 | // Calculate and decode a delta from the variable length integer.
497 | $olddelta = $delta;
498 | $w = 1;
499 | $x = 0;
500 | do
501 | {
502 | $x += self::PUNYCODE_BASE;
503 |
504 | $cp = $data[$pos];
505 | $pos++;
506 |
507 | if ($cp >= ord("a") && $cp <= ord("z")) $digit = $cp - ord("a");
508 | else if ($cp >= ord("A") && $cp <= ord("Z")) $digit = $cp - ord("A");
509 | else if ($cp >= ord("0") && $cp <= ord("9")) $digit = $cp - ord("0") + 26;
510 | else return false;
511 |
512 | $delta += $digit * $w;
513 | if ($delta < 0) return false;
514 |
515 | if ($x <= $bias) $t = self::PUNYCODE_TMIN;
516 | else if ($x >= $bias + self::PUNYCODE_TMAX) $t = self::PUNYCODE_TMAX;
517 | else $t = $x - $bias;
518 |
519 | if ($digit < $t) break;
520 |
521 | $w *= (self::PUNYCODE_BASE - $t);
522 | if ($w < 0) return false;
523 | } while (1);
524 |
525 | // Adapt bias.
526 | $numhandled++;
527 | $bias = self::InternalPunycodeAdapt($delta - $olddelta, $numhandled, $first);
528 | $first = false;
529 |
530 | // Delta was supposed to wrap around from $numhandled to 0, incrementing $n each time, so fix that now.
531 | $n += (int)($delta / $numhandled);
532 | $delta %= $numhandled;
533 |
534 | // Insert $n (the code point) at the delta position.
535 | array_splice($data2, $delta, 0, array($n));
536 | $delta++;
537 | }
538 |
539 | $parts[$num] = self::Convert($data2, self::UTF32_ARRAY, self::UTF8);
540 | }
541 |
542 | return implode(".", $parts);
543 | }
544 |
545 | // RFC3492 adapt() function.
546 | protected static function InternalPunycodeAdapt($delta, $numpoints, $first)
547 | {
548 | $delta = ($first ? (int)($delta / self::PUNYCODE_DAMP) : $delta >> 1);
549 | $delta += (int)($delta / $numpoints);
550 |
551 | $y = self::PUNYCODE_BASE - self::PUNYCODE_TMIN;
552 |
553 | $condval = (int)(($y * self::PUNYCODE_TMAX) / 2);
554 | for ($x = 0; $delta > $condval; $x += self::PUNYCODE_BASE) $delta = (int)($delta / $y);
555 |
556 | return (int)($x + ((($y + 1) * $delta) / ($delta + self::PUNYCODE_SKEW)));
557 | }
558 | }
559 | ?>
--------------------------------------------------------------------------------
/sdks/php/utf_utils.php:
--------------------------------------------------------------------------------
1 | = 0x0300 && $val <= 0x036F) || ($val >= 0x1DC0 && $val <= 0x1DFF) || ($val >= 0x20D0 && $val <= 0x20FF) || ($val >= 0xFE20 && $val <= 0xFE2F));
21 | }
22 |
23 | public static function Convert($data, $srctype, $desttype)
24 | {
25 | $arr = is_array($data);
26 | if ($arr) $srctype = self::UTF32_ARRAY;
27 | $x = 0;
28 | $y = ($arr ? count($data) : strlen($data));
29 | $result = ($desttype === self::UTF32_ARRAY ? array() : "");
30 | if (!$arr && $srctype === self::UTF32_ARRAY) return $result;
31 |
32 | $first = true;
33 |
34 | if ($srctype === self::UTF8_BOM)
35 | {
36 | if (substr($data, 0, 3) === "\xEF\xBB\xBF") $x = 3;
37 |
38 | $srctype = self::UTF8;
39 | }
40 |
41 | if ($srctype === self::UTF16_BOM)
42 | {
43 | if (substr($data, 0, 2) === "\xFE\xFF")
44 | {
45 | $srctype = self::UTF16_BE;
46 | $x = 2;
47 | }
48 | else if (substr($data, 0, 2) === "\xFF\xFE")
49 | {
50 | $srctype = self::UTF16_LE;
51 | $x = 2;
52 | }
53 | else
54 | {
55 | $srctype = self::UTF16_LE;
56 | }
57 | }
58 |
59 | if ($srctype === self::UTF32_BOM)
60 | {
61 | if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
62 | {
63 | $srctype = self::UTF32_BE;
64 | $x = 4;
65 | }
66 | else if (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
67 | {
68 | $srctype = self::UTF32_LE;
69 | $x = 4;
70 | }
71 | else
72 | {
73 | $srctype = self::UTF32_LE;
74 | }
75 | }
76 |
77 | while ($x < $y)
78 | {
79 | // Read the next valid code point.
80 | $val = false;
81 |
82 | switch ($srctype)
83 | {
84 | case self::UTF8:
85 | {
86 | $tempchr = ord($data[$x]);
87 | if ($tempchr <= 0x7F)
88 | {
89 | $val = $tempchr;
90 | $x++;
91 | }
92 | else if ($tempchr < 0xC2) $x++;
93 | else
94 | {
95 | $left = $y - $x;
96 | if ($left < 2) $x++;
97 | else
98 | {
99 | $tempchr2 = ord($data[$x + 1]);
100 |
101 | if (($tempchr >= 0xC2 && $tempchr <= 0xDF) && ($tempchr2 >= 0x80 && $tempchr2 <= 0xBF))
102 | {
103 | $val = (($tempchr & 0x1F) << 6) | ($tempchr2 & 0x3F);
104 | $x += 2;
105 | }
106 | else if ($left < 3) $x++;
107 | else
108 | {
109 | $tempchr3 = ord($data[$x + 2]);
110 |
111 | if ($tempchr3 < 0x80 || $tempchr3 > 0xBF) $x++;
112 | else
113 | {
114 | if (($tempchr == 0xE0 && ($tempchr2 >= 0xA0 && $tempchr2 <= 0xBF)) || ((($tempchr >= 0xE1 && $tempchr <= 0xEC) || $tempchr == 0xEE || $tempchr == 0xEF) && ($tempchr2 >= 0x80 && $tempchr2 <= 0xBF)) || ($tempchr == 0xED && ($tempchr2 >= 0x80 && $tempchr2 <= 0x9F)))
115 | {
116 | $val = (($tempchr & 0x0F) << 12) | (($tempchr2 & 0x3F) << 6) | ($tempchr3 & 0x3F);
117 | $x += 3;
118 | }
119 | else if ($left < 4) $x++;
120 | else
121 | {
122 | $tempchr4 = ord($data[$x + 3]);
123 |
124 | if ($tempchr4 < 0x80 || $tempchr4 > 0xBF) $x++;
125 | else if (($tempchr == 0xF0 && ($tempchr2 >= 0x90 && $tempchr2 <= 0xBF)) || (($tempchr >= 0xF1 && $tempchr <= 0xF3) && ($tempchr2 >= 0x80 && $tempchr2 <= 0xBF)) || ($tempchr == 0xF4 && ($tempchr2 >= 0x80 && $tempchr2 <= 0x8F)))
126 | {
127 | $val = (($tempchr & 0x07) << 18) | (($tempchr2 & 0x3F) << 12) | (($tempchr3 & 0x3F) << 6) | ($tempchr4 & 0x3F);
128 | $x += 4;
129 | }
130 | else
131 | {
132 | $x++;
133 | }
134 | }
135 | }
136 | }
137 | }
138 | }
139 |
140 | break;
141 | }
142 | case self::UTF16_LE:
143 | {
144 | if ($x + 1 >= $y) $x = $y;
145 | else
146 | {
147 | $val = unpack("v", substr($data, $x, 2))[1];
148 | $x += 2;
149 |
150 | if ($val >= 0xD800 && $val <= 0xDBFF)
151 | {
152 | if ($x + 1 >= $y)
153 | {
154 | $x = $y;
155 | $val = false;
156 | }
157 | else
158 | {
159 | $val2 = unpack("v", substr($data, $x, 2))[1];
160 |
161 | if ($val2 < 0xDC00 || $val2 > 0xDFFF) $val = false;
162 | else
163 | {
164 | $val = ((($val - 0xD800) << 10) | ($val2 - 0xDC00)) + 0x10000;
165 | $x += 2;
166 | }
167 | }
168 | }
169 | }
170 |
171 | break;
172 | }
173 | case self::UTF16_BE:
174 | {
175 | if ($x + 1 >= $y) $x = $y;
176 | else
177 | {
178 | $val = unpack("n", substr($data, $x, 2))[1];
179 | $x += 2;
180 |
181 | if ($val >= 0xD800 && $val <= 0xDBFF)
182 | {
183 | if ($x + 1 >= $y)
184 | {
185 | $x = $y;
186 | $val = false;
187 | }
188 | else
189 | {
190 | $val2 = unpack("n", substr($data, $x, 2))[1];
191 |
192 | if ($val2 < 0xDC00 || $val2 > 0xDFFF) $val = false;
193 | else
194 | {
195 | $val = ((($val - 0xD800) << 10) | ($val2 - 0xDC00)) + 0x10000;
196 | $x += 2;
197 | }
198 | }
199 | }
200 | }
201 |
202 | break;
203 | }
204 | case self::UTF32_LE:
205 | {
206 | if ($x + 3 >= $y) $x = $y;
207 | else
208 | {
209 | $val = unpack("V", substr($data, $x, 4))[1];
210 | $x += 4;
211 | }
212 |
213 | break;
214 | }
215 | case self::UTF32_BE:
216 | {
217 | if ($x + 3 >= $y) $x = $y;
218 | else
219 | {
220 | $val = unpack("N", substr($data, $x, 4))[1];
221 | $x += 4;
222 | }
223 |
224 | break;
225 | }
226 | case self::UTF32_ARRAY:
227 | {
228 | $val = (int)$data[$x];
229 | $x++;
230 |
231 | break;
232 | }
233 | default: $x = $y; break;
234 | }
235 |
236 | // Make sure it is a valid Unicode value.
237 | // 0xD800-0xDFFF are for UTF-16 surrogate pairs. Invalid characters.
238 | // 0xFDD0-0xFDEF are non-characters.
239 | // 0x*FFFE and 0x*FFFF are reserved.
240 | // The largest possible character is 0x10FFFF.
241 | // First character can't be a combining code point.
242 | if ($val !== false && !($val < 0 || ($val >= 0xD800 && $val <= 0xDFFF) || ($val >= 0xFDD0 && $val <= 0xFDEF) || ($val & 0xFFFE) == 0xFFFE || $val > 0x10FFFF || ($first && self::IsCombiningCodePoint($val))))
243 | {
244 | if ($first)
245 | {
246 | if ($desttype === self::UTF8_BOM)
247 | {
248 | $result .= "\xEF\xBB\xBF";
249 |
250 | $desttype = self::UTF8;
251 | }
252 |
253 | if ($desttype === self::UTF16_BOM)
254 | {
255 | $result .= "\xFF\xFE";
256 |
257 | $desttype = self::UTF16_LE;
258 | }
259 |
260 | if ($srctype === self::UTF32_BOM)
261 | {
262 | $result .= "\xFF\xFE\x00\x00";
263 |
264 | $desttype = self::UTF32_LE;
265 | }
266 |
267 | $first = false;
268 | }
269 |
270 | switch ($desttype)
271 | {
272 | case self::UTF8:
273 | {
274 | if ($val <= 0x7F) $result .= chr($val);
275 | else if ($val <= 0x7FF) $result .= chr(0xC0 | ($val >> 6)) . chr(0x80 | ($val & 0x3F));
276 | else if ($val <= 0xFFFF) $result .= chr(0xE0 | ($val >> 12)) . chr(0x80 | (($val >> 6) & 0x3F)) . chr(0x80 | ($val & 0x3F));
277 | else if ($val <= 0x10FFFF) $result .= chr(0xF0 | ($val >> 18)) . chr(0x80 | (($val >> 12) & 0x3F)) . chr(0x80 | (($val >> 6) & 0x3F)) . chr(0x80 | ($val & 0x3F));
278 |
279 | break;
280 | }
281 | case self::UTF16_LE:
282 | {
283 | if ($val <= 0xFFFF) $result .= pack("v", $val);
284 | else
285 | {
286 | $val -= 0x10000;
287 | $result .= pack("v", ((($val >> 10) & 0x3FF) + 0xD800));
288 | $result .= pack("v", (($val & 0x3FF) + 0xDC00));
289 | }
290 |
291 | break;
292 | }
293 | case self::UTF16_BE:
294 | {
295 | if ($val <= 0xFFFF) $result .= pack("n", $val);
296 | else
297 | {
298 | $val -= 0x10000;
299 | $result .= pack("n", ((($val >> 10) & 0x3FF) + 0xD800));
300 | $result .= pack("n", (($val & 0x3FF) + 0xDC00));
301 | }
302 |
303 | break;
304 | }
305 | case self::UTF32_LE:
306 | {
307 | $result .= pack("V", $val);
308 |
309 | break;
310 | }
311 | case self::UTF32_BE:
312 | {
313 | $result .= pack("N", $val);
314 |
315 | break;
316 | }
317 | case self::UTF32_ARRAY:
318 | {
319 | $result[] = $val;
320 |
321 | break;
322 | }
323 | default: $x = $y; break;
324 | }
325 | }
326 | }
327 |
328 | return $result;
329 | }
330 |
331 |
332 | protected const PUNYCODE_BASE = 36;
333 | protected const PUNYCODE_TMIN = 1;
334 | protected const PUNYCODE_TMAX = 26;
335 | protected const PUNYCODE_SKEW = 38;
336 | protected const PUNYCODE_DAMP = 700;
337 | protected const PUNYCODE_INITIAL_BIAS = 72;
338 | protected const PUNYCODE_INITIAL_N = 0x80;
339 | protected const PUNYCODE_DIGIT_MAP = "abcdefghijklmnopqrstuvwxyz0123456789";
340 |
341 | public static function ConvertToPunycode($domain)
342 | {
343 | // Reject invalid domain name lengths.
344 | if (strlen($domain) > 255) return false;
345 |
346 | $parts = explode(".", $domain);
347 |
348 | foreach ($parts as $num => $part)
349 | {
350 | // Reject invalid label lengths.
351 | $y = strlen($part);
352 | if ($y > 63) return false;
353 |
354 | // Skip already encoded portions.
355 | if (substr($part, 0, 4) === "xn--") continue;
356 |
357 | // Convert UTF-8 to UTF-32 code points.
358 | $data = self::Convert($part, self::UTF8, self::UTF32_ARRAY);
359 |
360 | // Handle ASCII code points.
361 | $part2 = "";
362 | foreach ($data as $cp)
363 | {
364 | if ($cp <= 0x7F) $part2 .= strtolower(chr($cp));
365 | }
366 |
367 | $numhandled = strlen($part2);
368 | $y = count($data);
369 |
370 | if ($numhandled >= $y)
371 | {
372 | $parts[$num] = $part2;
373 |
374 | continue;
375 | }
376 |
377 | if ($numhandled) $part2 .= "-";
378 |
379 | $part2 = "xn--" . $part2;
380 |
381 | if (strlen($part2) > 63) return false;
382 |
383 | $bias = self::PUNYCODE_INITIAL_BIAS;
384 | $n = self::PUNYCODE_INITIAL_N;
385 | $delta = 0;
386 | $first = true;
387 |
388 | while ($numhandled < $y)
389 | {
390 | // Find the next largest unhandled code point.
391 | $cp2 = 0x01000000;
392 | foreach ($data as $cp)
393 | {
394 | if ($cp >= $n && $cp2 > $cp) $cp2 = $cp;
395 | }
396 |
397 | // Increase delta but prevent overflow.
398 | $delta += ($cp2 - $n) * ($numhandled + 1);
399 | if ($delta < 0) return false;
400 | $n = $cp2;
401 |
402 | foreach ($data as $cp)
403 | {
404 | if ($cp < $n)
405 | {
406 | $delta++;
407 |
408 | if ($delta < 0) return false;
409 | }
410 | else if ($cp === $n)
411 | {
412 | // Calculate and encode a variable length integer from the delta.
413 | $q = $delta;
414 | $x = 0;
415 | do
416 | {
417 | $x += self::PUNYCODE_BASE;
418 |
419 | if ($x <= $bias) $t = self::PUNYCODE_TMIN;
420 | else if ($x >= $bias + self::PUNYCODE_TMAX) $t = self::PUNYCODE_TMAX;
421 | else $t = $x - $bias;
422 |
423 | if ($q < $t) break;
424 |
425 | $part2 .= self::PUNYCODE_DIGIT_MAP[$t + (($q - $t) % (self::PUNYCODE_BASE - $t))];
426 |
427 | $q = (int)(($q - $t) / (self::PUNYCODE_BASE - $t));
428 |
429 | if (strlen($part2) > 63) return false;
430 | } while (1);
431 |
432 | $part2 .= self::PUNYCODE_DIGIT_MAP[$q];
433 | if (strlen($part2) > 63) return false;
434 |
435 | // Adapt bias.
436 | $numhandled++;
437 | $bias = self::InternalPunycodeAdapt($delta, $numhandled, $first);
438 | $delta = 0;
439 | $first = false;
440 | }
441 | }
442 |
443 | $delta++;
444 | $n++;
445 | }
446 |
447 | $parts[$num] = $part2;
448 | }
449 |
450 | return implode(".", $parts);
451 | }
452 |
453 | public static function ConvertFromPunycode($domain)
454 | {
455 | // Reject invalid domain name lengths.
456 | if (strlen($domain) > 255) return false;
457 |
458 | $parts = explode(".", $domain);
459 |
460 | foreach ($parts as $num => $part)
461 | {
462 | // Reject invalid label lengths.
463 | $y = strlen($part);
464 | if ($y > 63) return false;
465 |
466 | // Skip unencoded portions.
467 | if (substr($part, 0, 4) !== "xn--") continue;
468 |
469 | $part = substr($part, 4);
470 |
471 | // Convert UTF-8 to UTF-32 code points.
472 | $data = self::Convert($part, self::UTF8, self::UTF32_ARRAY);
473 |
474 | // Handle ASCII code points.
475 | $hyphen = ord("-");
476 | for ($x = count($data); $x && $data[$x - 1] !== $hyphen; $x--);
477 | if (!$x) $data2 = array();
478 | else
479 | {
480 | $data2 = array_splice($data, 0, $x - 1);
481 |
482 | array_shift($data);
483 | }
484 |
485 | $numhandled = count($data2);
486 |
487 | $bias = self::PUNYCODE_INITIAL_BIAS;
488 | $n = self::PUNYCODE_INITIAL_N;
489 | $delta = 0;
490 | $first = true;
491 |
492 | $pos = 0;
493 | $y = count($data);
494 | while ($pos < $y)
495 | {
496 | // Calculate and decode a delta from the variable length integer.
497 | $olddelta = $delta;
498 | $w = 1;
499 | $x = 0;
500 | do
501 | {
502 | $x += self::PUNYCODE_BASE;
503 |
504 | $cp = $data[$pos];
505 | $pos++;
506 |
507 | if ($cp >= ord("a") && $cp <= ord("z")) $digit = $cp - ord("a");
508 | else if ($cp >= ord("A") && $cp <= ord("Z")) $digit = $cp - ord("A");
509 | else if ($cp >= ord("0") && $cp <= ord("9")) $digit = $cp - ord("0") + 26;
510 | else return false;
511 |
512 | $delta += $digit * $w;
513 | if ($delta < 0) return false;
514 |
515 | if ($x <= $bias) $t = self::PUNYCODE_TMIN;
516 | else if ($x >= $bias + self::PUNYCODE_TMAX) $t = self::PUNYCODE_TMAX;
517 | else $t = $x - $bias;
518 |
519 | if ($digit < $t) break;
520 |
521 | $w *= (self::PUNYCODE_BASE - $t);
522 | if ($w < 0) return false;
523 | } while (1);
524 |
525 | // Adapt bias.
526 | $numhandled++;
527 | $bias = self::InternalPunycodeAdapt($delta - $olddelta, $numhandled, $first);
528 | $first = false;
529 |
530 | // Delta was supposed to wrap around from $numhandled to 0, incrementing $n each time, so fix that now.
531 | $n += (int)($delta / $numhandled);
532 | $delta %= $numhandled;
533 |
534 | // Insert $n (the code point) at the delta position.
535 | array_splice($data2, $delta, 0, array($n));
536 | $delta++;
537 | }
538 |
539 | $parts[$num] = self::Convert($data2, self::UTF32_ARRAY, self::UTF8);
540 | }
541 |
542 | return implode(".", $parts);
543 | }
544 |
545 | // RFC3492 adapt() function.
546 | protected static function InternalPunycodeAdapt($delta, $numpoints, $first)
547 | {
548 | $delta = ($first ? (int)($delta / self::PUNYCODE_DAMP) : $delta >> 1);
549 | $delta += (int)($delta / $numpoints);
550 |
551 | $y = self::PUNYCODE_BASE - self::PUNYCODE_TMIN;
552 |
553 | $condval = (int)(($y * self::PUNYCODE_TMAX) / 2);
554 | for ($x = 0; $delta > $condval; $x += self::PUNYCODE_BASE) $delta = (int)($delta / $y);
555 |
556 | return (int)($x + ((($y + 1) * $delta) / ($delta + self::PUNYCODE_SKEW)));
557 | }
558 | }
559 | ?>
--------------------------------------------------------------------------------