├── LICENSE └── modules └── servers └── mailcow ├── lib ├── Curl │ ├── ArrayUtil.php │ ├── CaseInsensitiveArray.php │ ├── Curl.php │ └── MultiCurl.php ├── MailcowAPI.php └── README.md ├── mailcow.php └── templates ├── error.tpl ├── manage.tpl └── overview.tpl /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 WHMCS, Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /modules/servers/mailcow/lib/Curl/ArrayUtil.php: -------------------------------------------------------------------------------- 1 | $value) { 54 | if (is_scalar($value)) { 55 | if ($prefix) { 56 | $return[$prefix . '[' . $key . ']'] = $value; 57 | } else { 58 | $return[$key] = $value; 59 | } 60 | } else { 61 | if ($value instanceof \CURLFile) { 62 | $return[$key] = $value; 63 | } else { 64 | $return = array_merge( 65 | $return, 66 | self::array_flatten_multidim( 67 | $value, 68 | $prefix ? $prefix . '[' . $key . ']' : $key 69 | ) 70 | ); 71 | } 72 | } 73 | } 74 | } 75 | } elseif ($array === null) { 76 | $return[$prefix] = $array; 77 | } 78 | return $return; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /modules/servers/mailcow/lib/Curl/CaseInsensitiveArray.php: -------------------------------------------------------------------------------- 1 | $value) { 46 | $this->offsetSet($key, $value); 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Offset Set 53 | * 54 | * Set data at a specified Offset. Converts the offset to lower-case, and 55 | * stores the Case-Sensitive Offset and the Data at the lower-case indexes 56 | * in $this->keys and @this->data. 57 | * 58 | * @see https://secure.php.net/manual/en/arrayaccess.offseteset.php 59 | * 60 | * @param string $offset The offset to store the data at (case-insensitive). 61 | * @param mixed $value The data to store at the specified offset. 62 | * 63 | * @return void 64 | * 65 | * @access public 66 | */ 67 | public function offsetSet($offset, $value) 68 | { 69 | if ($offset === null) { 70 | $this->data[] = $value; 71 | } else { 72 | $offsetlower = strtolower($offset); 73 | $this->data[$offsetlower] = $value; 74 | $this->keys[$offsetlower] = $offset; 75 | } 76 | } 77 | 78 | /** 79 | * Offset Exists 80 | * 81 | * Checks if the Offset exists in data storage. The index is looked up with 82 | * the lower-case version of the provided offset. 83 | * 84 | * @see https://secure.php.net/manual/en/arrayaccess.offsetexists.php 85 | * 86 | * @param string $offset Offset to check 87 | * 88 | * @return bool If the offset exists. 89 | * 90 | * @access public 91 | */ 92 | public function offsetExists($offset) 93 | { 94 | return (bool) array_key_exists(strtolower($offset), $this->data); 95 | } 96 | 97 | /** 98 | * Offset Unset 99 | * 100 | * Unsets the specified offset. Converts the provided offset to lowercase, 101 | * and unsets the Case-Sensitive Key, as well as the stored data. 102 | * 103 | * @see https://secure.php.net/manual/en/arrayaccess.offsetunset.php 104 | * 105 | * @param string $offset The offset to unset. 106 | * 107 | * @return void 108 | * 109 | * @access public 110 | */ 111 | public function offsetUnset($offset) 112 | { 113 | $offsetlower = strtolower($offset); 114 | unset($this->data[$offsetlower]); 115 | unset($this->keys[$offsetlower]); 116 | } 117 | 118 | /** 119 | * Offset Get 120 | * 121 | * Return the stored data at the provided offset. The offset is converted to 122 | * lowercase and the lookup is done on the Data store directly. 123 | * 124 | * @see https://secure.php.net/manual/en/arrayaccess.offsetget.php 125 | * 126 | * @param string $offset Offset to lookup. 127 | * 128 | * @return mixed The data stored at the offset. 129 | * 130 | * @access public 131 | */ 132 | public function offsetGet($offset) 133 | { 134 | $offsetlower = strtolower($offset); 135 | return isset($this->data[$offsetlower]) ? $this->data[$offsetlower] : null; 136 | } 137 | 138 | /** 139 | * Count 140 | * 141 | * @see https://secure.php.net/manual/en/countable.count.php 142 | * 143 | * @param void 144 | * 145 | * @return int The number of elements stored in the Array. 146 | * 147 | * @access public 148 | */ 149 | public function count() 150 | { 151 | return (int) count($this->data); 152 | } 153 | 154 | /** 155 | * Current 156 | * 157 | * @see https://secure.php.net/manual/en/iterator.current.php 158 | * 159 | * @param void 160 | * 161 | * @return mixed Data at the current position. 162 | * 163 | * @access public 164 | */ 165 | public function current() 166 | { 167 | return current($this->data); 168 | } 169 | 170 | /** 171 | * Next 172 | * 173 | * @see https://secure.php.net/manual/en/iterator.next.php 174 | * 175 | * @param void 176 | * 177 | * @return void 178 | * 179 | * @access public 180 | */ 181 | public function next() 182 | { 183 | next($this->data); 184 | } 185 | 186 | /** 187 | * Key 188 | * 189 | * @see https://secure.php.net/manual/en/iterator.key.php 190 | * 191 | * @param void 192 | * 193 | * @return mixed Case-Sensitive key at current position. 194 | * 195 | * @access public 196 | */ 197 | public function key() 198 | { 199 | $key = key($this->data); 200 | return isset($this->keys[$key]) ? $this->keys[$key] : $key; 201 | } 202 | 203 | /** 204 | * Valid 205 | * 206 | * @see https://secure.php.net/manual/en/iterator.valid.php 207 | * 208 | * @return bool If the current position is valid. 209 | * 210 | * @access public 211 | */ 212 | public function valid() 213 | { 214 | return (bool) !(key($this->data) === null); 215 | } 216 | 217 | /** 218 | * Rewind 219 | * 220 | * @see https://secure.php.net/manual/en/iterator.rewind.php 221 | * 222 | * @param void 223 | * 224 | * @return void 225 | * 226 | * @access public 227 | */ 228 | public function rewind() 229 | { 230 | reset($this->data); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /modules/servers/mailcow/lib/Curl/Curl.php: -------------------------------------------------------------------------------- 1 | 56 | // CTL = 58 | // separators = "(" | ")" | "<" | ">" | "@" 59 | // | "," | ";" | ":" | "\" | <"> 60 | // | "/" | "[" | "]" | "?" | "=" 61 | // | "{" | "}" | SP | HT 62 | // SP = 63 | // HT = 64 | // <"> = 65 | '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 66 | 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 67 | 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 68 | 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~', 69 | ); 70 | public static $RFC6265 = array( 71 | // RFC6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash". 72 | // %x21 73 | '!', 74 | // %x23-2B 75 | '#', '$', '%', '&', "'", '(', ')', '*', '+', 76 | // %x2D-3A 77 | '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 78 | // %x3C-5B 79 | '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 80 | 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', 81 | // %x5D-7E 82 | ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 83 | 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 84 | ); 85 | 86 | private static $deferredProperties = array( 87 | 'effectiveUrl', 88 | 'rfc2616', 89 | 'rfc6265', 90 | 'totalTime', 91 | ); 92 | 93 | /** 94 | * Construct 95 | * 96 | * @access public 97 | * @param $base_url 98 | * @throws \ErrorException 99 | */ 100 | public function __construct($base_url = null) 101 | { 102 | if (!extension_loaded('curl')) { 103 | throw new \ErrorException('cURL library is not loaded'); 104 | } 105 | 106 | $this->curl = curl_init(); 107 | $this->id = uniqid('', true); 108 | $this->setDefaultUserAgent(); 109 | $this->setDefaultJsonDecoder(); 110 | $this->setDefaultXmlDecoder(); 111 | $this->setDefaultTimeout(); 112 | $this->setOpt(CURLINFO_HEADER_OUT, true); 113 | $this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'headerCallback')); 114 | $this->setOpt(CURLOPT_RETURNTRANSFER, true); 115 | $this->headers = new CaseInsensitiveArray(); 116 | $this->setUrl($base_url); 117 | } 118 | 119 | /** 120 | * Before Send 121 | * 122 | * @access public 123 | * @param $callback 124 | */ 125 | public function beforeSend($callback) 126 | { 127 | $this->beforeSendFunction = $callback; 128 | } 129 | 130 | /** 131 | * Build Post Data 132 | * 133 | * @access public 134 | * @param $data 135 | * 136 | * @return array|string 137 | */ 138 | public function buildPostData($data) 139 | { 140 | $binary_data = false; 141 | if (is_array($data)) { 142 | // Return JSON-encoded string when the request's content-type is JSON. 143 | if (isset($this->headers['Content-Type']) && 144 | preg_match($this->jsonPattern, $this->headers['Content-Type'])) { 145 | $json_str = json_encode($data); 146 | if (!($json_str === false)) { 147 | $data = $json_str; 148 | } 149 | } else { 150 | // Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch, 151 | // CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are 152 | // referenced. 153 | if (ArrayUtil::is_array_multidim($data)) { 154 | $data = ArrayUtil::array_flatten_multidim($data); 155 | } 156 | 157 | // Modify array values to ensure any referenced files are properly handled depending on the support of 158 | // the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the 159 | // @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore 160 | // non-file values prefixed with the @ character. 161 | foreach ($data as $key => $value) { 162 | if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) { 163 | $binary_data = true; 164 | if (class_exists('CURLFile')) { 165 | $data[$key] = new \CURLFile(substr($value, 1)); 166 | } 167 | } elseif ($value instanceof \CURLFile) { 168 | $binary_data = true; 169 | } 170 | } 171 | } 172 | } 173 | 174 | if (!$binary_data && (is_array($data) || is_object($data))) { 175 | $data = http_build_query($data, '', '&'); 176 | } 177 | 178 | return $data; 179 | } 180 | 181 | /** 182 | * Call 183 | * 184 | * @access public 185 | */ 186 | public function call() 187 | { 188 | $args = func_get_args(); 189 | $function = array_shift($args); 190 | if (is_callable($function)) { 191 | array_unshift($args, $this); 192 | call_user_func_array($function, $args); 193 | } 194 | } 195 | 196 | /** 197 | * Close 198 | * 199 | * @access public 200 | */ 201 | public function close() 202 | { 203 | if (is_resource($this->curl)) { 204 | curl_close($this->curl); 205 | } 206 | $this->options = null; 207 | $this->jsonDecoder = null; 208 | $this->xmlDecoder = null; 209 | $this->defaultDecoder = null; 210 | } 211 | 212 | /** 213 | * Complete 214 | * 215 | * @access public 216 | * @param $callback 217 | */ 218 | public function complete($callback) 219 | { 220 | $this->completeFunction = $callback; 221 | } 222 | 223 | /** 224 | * Progress 225 | * 226 | * @access public 227 | * @param $callback 228 | */ 229 | public function progress($callback) 230 | { 231 | $this->setOpt(CURLOPT_PROGRESSFUNCTION, $callback); 232 | $this->setOpt(CURLOPT_NOPROGRESS, false); 233 | } 234 | 235 | /** 236 | * Delete 237 | * 238 | * @access public 239 | * @param $url 240 | * @param $query_parameters 241 | * @param $data 242 | * 243 | * @return string 244 | */ 245 | public function delete($url, $query_parameters = array(), $data = array()) 246 | { 247 | if (is_array($url)) { 248 | $data = $query_parameters; 249 | $query_parameters = $url; 250 | $url = $this->baseUrl; 251 | } 252 | 253 | $this->setUrl($url, $query_parameters); 254 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); 255 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); 256 | return $this->exec(); 257 | } 258 | 259 | /** 260 | * Download Complete 261 | * 262 | * @access private 263 | * @param $fh 264 | */ 265 | private function downloadComplete($fh) 266 | { 267 | if (!$this->error && $this->downloadCompleteFunction) { 268 | rewind($fh); 269 | $this->call($this->downloadCompleteFunction, $fh); 270 | $this->downloadCompleteFunction = null; 271 | } 272 | 273 | if (is_resource($fh)) { 274 | fclose($fh); 275 | } 276 | 277 | // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the 278 | // PHP script from stdin. Using null causes "Warning: curl_setopt(): 279 | // supplied argument is not a valid File-Handle resource". 280 | if (!defined('STDOUT')) { 281 | define('STDOUT', fopen('php://stdout', 'w')); 282 | } 283 | 284 | // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE 285 | // resource has gone away, resetting to default". 286 | $this->setOpt(CURLOPT_FILE, STDOUT); 287 | 288 | // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent 289 | // responses as the return value of curl_exec(). Without this, 290 | // curl_exec() will revert to returning boolean values. 291 | $this->setOpt(CURLOPT_RETURNTRANSFER, true); 292 | } 293 | 294 | /** 295 | * Download 296 | * 297 | * @access public 298 | * @param $url 299 | * @param $mixed_filename 300 | * 301 | * @return boolean 302 | */ 303 | public function download($url, $mixed_filename) 304 | { 305 | if (is_callable($mixed_filename)) { 306 | $this->downloadCompleteFunction = $mixed_filename; 307 | $fh = tmpfile(); 308 | } else { 309 | $filename = $mixed_filename; 310 | 311 | // Use a temporary file when downloading. Not using a temporary file can cause an error when an existing 312 | // file has already fully completed downloading and a new download is started with the same destination save 313 | // path. The download request will include header "Range: bytes=$filesize-" which is syntactically valid, 314 | // but unsatisfiable. 315 | $download_filename = $filename . '.pccdownload'; 316 | 317 | $mode = 'wb'; 318 | // Attempt to resume download only when a temporary download file exists and is not empty. 319 | if (file_exists($download_filename) && $filesize = filesize($download_filename)) { 320 | $mode = 'ab'; 321 | $first_byte_position = $filesize; 322 | $range = $first_byte_position . '-'; 323 | $this->setOpt(CURLOPT_RANGE, $range); 324 | } 325 | $fh = fopen($download_filename, $mode); 326 | 327 | // Move the downloaded temporary file to the destination save path. 328 | $this->downloadCompleteFunction = function ($fh) use ($download_filename, $filename) { 329 | rename($download_filename, $filename); 330 | }; 331 | } 332 | 333 | $this->setOpt(CURLOPT_FILE, $fh); 334 | $this->get($url); 335 | $this->downloadComplete($fh); 336 | 337 | return ! $this->error; 338 | } 339 | 340 | /** 341 | * Error 342 | * 343 | * @access public 344 | * @param $callback 345 | */ 346 | public function error($callback) 347 | { 348 | $this->errorFunction = $callback; 349 | } 350 | 351 | /** 352 | * Exec 353 | * 354 | * @access public 355 | * @param $ch 356 | * 357 | * @return mixed Returns the value provided by parseResponse. 358 | */ 359 | public function exec($ch = null) 360 | { 361 | if ($ch === null) { 362 | $this->responseCookies = array(); 363 | $this->call($this->beforeSendFunction); 364 | $this->rawResponse = curl_exec($this->curl); 365 | $this->curlErrorCode = curl_errno($this->curl); 366 | $this->curlErrorMessage = curl_error($this->curl); 367 | } else { 368 | $this->rawResponse = curl_multi_getcontent($ch); 369 | $this->curlErrorMessage = curl_error($ch); 370 | } 371 | $this->curlError = !($this->curlErrorCode === 0); 372 | 373 | // Include additional error code information in error message when possible. 374 | if ($this->curlError && function_exists('curl_strerror')) { 375 | $this->curlErrorMessage = 376 | curl_strerror($this->curlErrorCode) . ( 377 | empty($this->curlErrorMessage) ? '' : ': ' . $this->curlErrorMessage 378 | ); 379 | } 380 | 381 | $this->httpStatusCode = $this->getInfo(CURLINFO_HTTP_CODE); 382 | $this->httpError = in_array(floor($this->httpStatusCode / 100), array(4, 5)); 383 | $this->error = $this->curlError || $this->httpError; 384 | $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0; 385 | 386 | // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders 387 | // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);). 388 | if ($this->getOpt(CURLINFO_HEADER_OUT) === true) { 389 | $this->requestHeaders = $this->parseRequestHeaders($this->getInfo(CURLINFO_HEADER_OUT)); 390 | } 391 | $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders); 392 | $this->response = $this->parseResponse($this->responseHeaders, $this->rawResponse); 393 | 394 | $this->httpErrorMessage = ''; 395 | if ($this->error) { 396 | if (isset($this->responseHeaders['Status-Line'])) { 397 | $this->httpErrorMessage = $this->responseHeaders['Status-Line']; 398 | } 399 | } 400 | $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage; 401 | 402 | if (!$this->error) { 403 | $this->call($this->successFunction); 404 | } else { 405 | $this->call($this->errorFunction); 406 | } 407 | 408 | $this->call($this->completeFunction); 409 | 410 | // Close open file handles and reset the curl instance. 411 | if (!($this->fileHandle === null)) { 412 | $this->downloadComplete($this->fileHandle); 413 | } 414 | 415 | return $this->response; 416 | } 417 | 418 | /** 419 | * Get 420 | * 421 | * @access public 422 | * @param $url 423 | * @param $data 424 | * 425 | * @return mixed Returns the value provided by exec. 426 | */ 427 | public function get($url, $data = array()) 428 | { 429 | if (is_array($url)) { 430 | $data = $url; 431 | $url = $this->baseUrl; 432 | } 433 | $this->setUrl($url, $data); 434 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); 435 | $this->setOpt(CURLOPT_HTTPGET, true); 436 | return $this->exec(); 437 | } 438 | 439 | /** 440 | * Get Info 441 | * 442 | * @access public 443 | * @param $opt 444 | * 445 | * @return mixed 446 | */ 447 | public function getInfo($opt = null) 448 | { 449 | $args = array(); 450 | $args[] = $this->curl; 451 | 452 | if (func_num_args()) { 453 | $args[] = $opt; 454 | } 455 | 456 | return call_user_func_array('curl_getinfo', $args); 457 | } 458 | 459 | /** 460 | * Get Opt 461 | * 462 | * @access public 463 | * @param $option 464 | * 465 | * @return mixed 466 | */ 467 | public function getOpt($option) 468 | { 469 | return isset($this->options[$option]) ? $this->options[$option] : null; 470 | } 471 | 472 | /** 473 | * Head 474 | * 475 | * @access public 476 | * @param $url 477 | * @param $data 478 | * 479 | * @return string 480 | */ 481 | public function head($url, $data = array()) 482 | { 483 | if (is_array($url)) { 484 | $data = $url; 485 | $url = $this->baseUrl; 486 | } 487 | $this->setUrl($url, $data); 488 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); 489 | $this->setOpt(CURLOPT_NOBODY, true); 490 | return $this->exec(); 491 | } 492 | 493 | /** 494 | * Header Callback 495 | * 496 | * @access public 497 | * @param $ch 498 | * @param $header 499 | * 500 | * @return integer 501 | */ 502 | public function headerCallback($ch, $header) 503 | { 504 | if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) { 505 | $this->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B"); 506 | } 507 | $this->rawResponseHeaders .= $header; 508 | return strlen($header); 509 | } 510 | 511 | /** 512 | * Options 513 | * 514 | * @access public 515 | * @param $url 516 | * @param $data 517 | * 518 | * @return string 519 | */ 520 | public function options($url, $data = array()) 521 | { 522 | if (is_array($url)) { 523 | $data = $url; 524 | $url = $this->baseUrl; 525 | } 526 | $this->setUrl($url, $data); 527 | $this->removeHeader('Content-Length'); 528 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); 529 | return $this->exec(); 530 | } 531 | 532 | /** 533 | * Patch 534 | * 535 | * @access public 536 | * @param $url 537 | * @param $data 538 | * 539 | * @return string 540 | */ 541 | public function patch($url, $data = array()) 542 | { 543 | if (is_array($url)) { 544 | $data = $url; 545 | $url = $this->baseUrl; 546 | } 547 | 548 | if (is_array($data) && empty($data)) { 549 | $this->removeHeader('Content-Length'); 550 | } 551 | 552 | $this->setUrl($url); 553 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); 554 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); 555 | return $this->exec(); 556 | } 557 | 558 | /** 559 | * Post 560 | * 561 | * @access public 562 | * @param $url 563 | * @param $data 564 | * @param $follow_303_with_post 565 | * If true, will cause 303 redirections to be followed using a POST request (default: false). 566 | * Notes: 567 | * - Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. 568 | * - According to the HTTP specs (see [1]), a 303 redirection should be followed using 569 | * the GET method. 301 and 302 must not. 570 | * - In order to force a 303 redirection to be performed using the same method, the 571 | * underlying cURL object must be set in a special state (the CURLOPT_CURSTOMREQUEST 572 | * option must be set to the method to use after the redirection). Due to a limitation 573 | * of the cURL extension of PHP < 5.5.11 ([2], [3]) and of HHVM, it is not possible 574 | * to reset this option. Using these PHP engines, it is therefore impossible to 575 | * restore this behavior on an existing php-curl-class Curl object. 576 | * 577 | * @return string 578 | * 579 | * [1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2 580 | * [2] https://github.com/php/php-src/pull/531 581 | * [3] http://php.net/ChangeLog-5.php#5.5.11 582 | */ 583 | public function post($url, $data = array(), $follow_303_with_post = false) 584 | { 585 | if (is_array($url)) { 586 | $follow_303_with_post = (bool)$data; 587 | $data = $url; 588 | $url = $this->baseUrl; 589 | } 590 | 591 | $this->setUrl($url); 592 | 593 | if ($follow_303_with_post) { 594 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); 595 | } else { 596 | if (isset($this->options[CURLOPT_CUSTOMREQUEST])) { 597 | if ((version_compare(PHP_VERSION, '5.5.11') < 0) || defined('HHVM_VERSION')) { 598 | trigger_error( 599 | 'Due to technical limitations of PHP <= 5.5.11 and HHVM, it is not possible to ' 600 | . 'perform a post-redirect-get request using a php-curl-class Curl object that ' 601 | . 'has already been used to perform other types of requests. Either use a new ' 602 | . 'php-curl-class Curl object or upgrade your PHP engine.', 603 | E_USER_ERROR 604 | ); 605 | } else { 606 | $this->setOpt(CURLOPT_CUSTOMREQUEST, null); 607 | } 608 | } 609 | } 610 | 611 | $this->setOpt(CURLOPT_POST, true); 612 | $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); 613 | return $this->exec(); 614 | } 615 | 616 | /** 617 | * Put 618 | * 619 | * @access public 620 | * @param $url 621 | * @param $data 622 | * 623 | * @return string 624 | */ 625 | public function put($url, $data = array()) 626 | { 627 | if (is_array($url)) { 628 | $data = $url; 629 | $url = $this->baseUrl; 630 | } 631 | $this->setUrl($url); 632 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); 633 | $put_data = $this->buildPostData($data); 634 | if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { 635 | if (is_string($put_data)) { 636 | $this->setHeader('Content-Length', strlen($put_data)); 637 | } 638 | } 639 | if (!empty($put_data)) { 640 | $this->setOpt(CURLOPT_POSTFIELDS, $put_data); 641 | } 642 | return $this->exec(); 643 | } 644 | 645 | /** 646 | * Search 647 | * 648 | * @access public 649 | * @param $url 650 | * @param $data 651 | * 652 | * @return string 653 | */ 654 | public function search($url, $data = array()) 655 | { 656 | if (is_array($url)) { 657 | $data = $url; 658 | $url = $this->baseUrl; 659 | } 660 | $this->setUrl($url); 661 | $this->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); 662 | $put_data = $this->buildPostData($data); 663 | if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { 664 | if (is_string($put_data)) { 665 | $this->setHeader('Content-Length', strlen($put_data)); 666 | } 667 | } 668 | if (!empty($put_data)) { 669 | $this->setOpt(CURLOPT_POSTFIELDS, $put_data); 670 | } 671 | return $this->exec(); 672 | } 673 | 674 | /** 675 | * Set Basic Authentication 676 | * 677 | * @access public 678 | * @param $username 679 | * @param $password 680 | */ 681 | public function setBasicAuthentication($username, $password = '') 682 | { 683 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 684 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 685 | } 686 | 687 | /** 688 | * Set Digest Authentication 689 | * 690 | * @access public 691 | * @param $username 692 | * @param $password 693 | */ 694 | public function setDigestAuthentication($username, $password = '') 695 | { 696 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 697 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 698 | } 699 | 700 | /** 701 | * Set Cookie 702 | * 703 | * @access public 704 | * @param $key 705 | * @param $value 706 | */ 707 | public function setCookie($key, $value) 708 | { 709 | $name_chars = array(); 710 | foreach (str_split($key) as $name_char) { 711 | if (!isset($this->rfc2616[$name_char])) { 712 | $name_chars[] = rawurlencode($name_char); 713 | } else { 714 | $name_chars[] = $name_char; 715 | } 716 | } 717 | 718 | $value_chars = array(); 719 | foreach (str_split($value) as $value_char) { 720 | if (!isset($this->rfc6265[$value_char])) { 721 | $value_chars[] = rawurlencode($value_char); 722 | } else { 723 | $value_chars[] = $value_char; 724 | } 725 | } 726 | 727 | $this->cookies[implode('', $name_chars)] = implode('', $value_chars); 728 | $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) { 729 | return $k . '=' . $v; 730 | }, array_keys($this->cookies), array_values($this->cookies)))); 731 | } 732 | 733 | /** 734 | * Set Cookies 735 | * 736 | * @access public 737 | * @param $cookies 738 | */ 739 | public function setCookies($cookies) 740 | { 741 | foreach ($cookies as $key => $value) { 742 | $name_chars = array(); 743 | foreach (str_split($key) as $name_char) { 744 | if (!isset($this->rfc2616[$name_char])) { 745 | $name_chars[] = rawurlencode($name_char); 746 | } else { 747 | $name_chars[] = $name_char; 748 | } 749 | } 750 | 751 | $value_chars = array(); 752 | foreach (str_split($value) as $value_char) { 753 | if (!isset($this->rfc6265[$value_char])) { 754 | $value_chars[] = rawurlencode($value_char); 755 | } else { 756 | $value_chars[] = $value_char; 757 | } 758 | } 759 | 760 | $this->cookies[implode('', $name_chars)] = implode('', $value_chars); 761 | } 762 | 763 | $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) { 764 | return $k . '=' . $v; 765 | }, array_keys($this->cookies), array_values($this->cookies)))); 766 | } 767 | 768 | /** 769 | * Get Cookie 770 | * 771 | * @access public 772 | * @param $key 773 | * 774 | * @return mixed 775 | */ 776 | public function getCookie($key) 777 | { 778 | return $this->getResponseCookie($key); 779 | } 780 | 781 | /** 782 | * Get Response Cookie 783 | * 784 | * @access public 785 | * @param $key 786 | * 787 | * @return mixed 788 | */ 789 | public function getResponseCookie($key) 790 | { 791 | return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null; 792 | } 793 | 794 | /** 795 | * Set Max Filesize 796 | * 797 | * @access public 798 | * @param $bytes 799 | */ 800 | public function setMaxFilesize($bytes) 801 | { 802 | // Make compatible with PHP version both before and after 5.5.0. PHP 5.5.0 added the cURL resource as the first 803 | // argument to the CURLOPT_PROGRESSFUNCTION callback. 804 | $gte_v550 = version_compare(PHP_VERSION, '5.5.0') >= 0; 805 | if ($gte_v550) { 806 | $callback = function ($resource, $download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { 807 | // Abort the transfer when $downloaded bytes exceeds maximum $bytes by returning a non-zero value. 808 | return $downloaded > $bytes ? 1 : 0; 809 | }; 810 | } else { 811 | $callback = function ($download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { 812 | return $downloaded > $bytes ? 1 : 0; 813 | }; 814 | } 815 | 816 | $this->progress($callback); 817 | } 818 | 819 | /** 820 | * Set Port 821 | * 822 | * @access public 823 | * @param $port 824 | */ 825 | public function setPort($port) 826 | { 827 | $this->setOpt(CURLOPT_PORT, intval($port)); 828 | } 829 | 830 | /** 831 | * Set Connect Timeout 832 | * 833 | * @access public 834 | * @param $seconds 835 | */ 836 | public function setConnectTimeout($seconds) 837 | { 838 | $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds); 839 | } 840 | 841 | /** 842 | * Set Cookie String 843 | * 844 | * @access public 845 | * @param $string 846 | * 847 | * @return bool 848 | */ 849 | public function setCookieString($string) 850 | { 851 | return $this->setOpt(CURLOPT_COOKIE, $string); 852 | } 853 | 854 | /** 855 | * Set Cookie File 856 | * 857 | * @access public 858 | * @param $cookie_file 859 | * 860 | * @return boolean 861 | */ 862 | public function setCookieFile($cookie_file) 863 | { 864 | return $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); 865 | } 866 | 867 | /** 868 | * Set Cookie Jar 869 | * 870 | * @access public 871 | * @param $cookie_jar 872 | * 873 | * @return boolean 874 | */ 875 | public function setCookieJar($cookie_jar) 876 | { 877 | return $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); 878 | } 879 | 880 | /** 881 | * Set Default JSON Decoder 882 | * 883 | * @access public 884 | * @param $assoc 885 | * @param $depth 886 | * @param $options 887 | */ 888 | public function setDefaultJsonDecoder() 889 | { 890 | $args = func_get_args(); 891 | $this->jsonDecoder = function ($response) use ($args) { 892 | array_unshift($args, $response); 893 | 894 | // Call json_decode() without the $options parameter in PHP 895 | // versions less than 5.4.0 as the $options parameter was added in 896 | // PHP version 5.4.0. 897 | if (version_compare(PHP_VERSION, '5.4.0', '<')) { 898 | $args = array_slice($args, 0, 3); 899 | } 900 | 901 | $json_obj = call_user_func_array('json_decode', $args); 902 | if (!($json_obj === null)) { 903 | $response = $json_obj; 904 | } 905 | return $response; 906 | }; 907 | } 908 | 909 | /** 910 | * Set Default XML Decoder 911 | * 912 | * @access public 913 | */ 914 | public function setDefaultXmlDecoder() 915 | { 916 | $this->xmlDecoder = function ($response) { 917 | $xml_obj = @simplexml_load_string($response); 918 | if (!($xml_obj === false)) { 919 | $response = $xml_obj; 920 | } 921 | return $response; 922 | }; 923 | } 924 | 925 | /** 926 | * Set Default Decoder 927 | * 928 | * @access public 929 | * @param $decoder string|callable 930 | */ 931 | public function setDefaultDecoder($decoder = 'json') 932 | { 933 | if (is_callable($decoder)) { 934 | $this->defaultDecoder = $decoder; 935 | } else { 936 | if ($decoder === 'json') { 937 | $this->defaultDecoder = $this->jsonDecoder; 938 | } elseif ($decoder === 'xml') { 939 | $this->defaultDecoder = $this->xmlDecoder; 940 | } 941 | } 942 | } 943 | 944 | /** 945 | * Set Default Timeout 946 | * 947 | * @access public 948 | */ 949 | public function setDefaultTimeout() 950 | { 951 | $this->setTimeout(self::DEFAULT_TIMEOUT); 952 | } 953 | 954 | /** 955 | * Set Default User Agent 956 | * 957 | * @access public 958 | */ 959 | public function setDefaultUserAgent() 960 | { 961 | $user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)'; 962 | $user_agent .= ' PHP/' . PHP_VERSION; 963 | $curl_version = curl_version(); 964 | $user_agent .= ' curl/' . $curl_version['version']; 965 | $this->setUserAgent($user_agent); 966 | } 967 | 968 | /** 969 | * Set Header 970 | * 971 | * Add extra header to include in the request. 972 | * 973 | * @access public 974 | * @param $key 975 | * @param $value 976 | */ 977 | public function setHeader($key, $value) 978 | { 979 | $this->headers[$key] = $value; 980 | $headers = array(); 981 | foreach ($this->headers as $key => $value) { 982 | $headers[] = $key . ': ' . $value; 983 | } 984 | $this->setOpt(CURLOPT_HTTPHEADER, $headers); 985 | } 986 | 987 | /** 988 | * Set Headers 989 | * 990 | * Add extra headers to include in the request. 991 | * 992 | * @access public 993 | * @param $headers 994 | */ 995 | public function setHeaders($headers) 996 | { 997 | foreach ($headers as $key => $value) { 998 | $this->headers[$key] = $value; 999 | } 1000 | 1001 | $headers = array(); 1002 | foreach ($this->headers as $key => $value) { 1003 | $headers[] = $key . ': ' . $value; 1004 | } 1005 | $this->setOpt(CURLOPT_HTTPHEADER, $headers); 1006 | } 1007 | 1008 | /** 1009 | * Set JSON Decoder 1010 | * 1011 | * @access public 1012 | * @param $function 1013 | */ 1014 | public function setJsonDecoder($function) 1015 | { 1016 | if (is_callable($function)) { 1017 | $this->jsonDecoder = $function; 1018 | } 1019 | } 1020 | 1021 | /** 1022 | * Set XML Decoder 1023 | * 1024 | * @access public 1025 | * @param $function 1026 | */ 1027 | public function setXmlDecoder($function) 1028 | { 1029 | if (is_callable($function)) { 1030 | $this->xmlDecoder = $function; 1031 | } 1032 | } 1033 | 1034 | /** 1035 | * Set Opt 1036 | * 1037 | * @access public 1038 | * @param $option 1039 | * @param $value 1040 | * 1041 | * @return boolean 1042 | */ 1043 | public function setOpt($option, $value) 1044 | { 1045 | $required_options = array( 1046 | CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER', 1047 | ); 1048 | 1049 | if (in_array($option, array_keys($required_options), true) && !($value === true)) { 1050 | trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING); 1051 | } 1052 | 1053 | $success = curl_setopt($this->curl, $option, $value); 1054 | if ($success) { 1055 | $this->options[$option] = $value; 1056 | } 1057 | return $success; 1058 | } 1059 | 1060 | /** 1061 | * Set Opts 1062 | * 1063 | * @access public 1064 | * @param $options 1065 | * 1066 | * @return boolean 1067 | * Returns true if all options were successfully set. If an option could not be successfully set, false is 1068 | * immediately returned, ignoring any future options in the options array. Similar to curl_setopt_array(). 1069 | */ 1070 | public function setOpts($options) 1071 | { 1072 | foreach ($options as $option => $value) { 1073 | if (!$this->setOpt($option, $value)) { 1074 | return false; 1075 | } 1076 | } 1077 | return true; 1078 | } 1079 | 1080 | /** 1081 | * Set Referer 1082 | * 1083 | * @access public 1084 | * @param $referer 1085 | */ 1086 | public function setReferer($referer) 1087 | { 1088 | $this->setReferrer($referer); 1089 | } 1090 | 1091 | /** 1092 | * Set Referrer 1093 | * 1094 | * @access public 1095 | * @param $referrer 1096 | */ 1097 | public function setReferrer($referrer) 1098 | { 1099 | $this->setOpt(CURLOPT_REFERER, $referrer); 1100 | } 1101 | 1102 | /** 1103 | * Set Timeout 1104 | * 1105 | * @access public 1106 | * @param $seconds 1107 | */ 1108 | public function setTimeout($seconds) 1109 | { 1110 | $this->setOpt(CURLOPT_TIMEOUT, $seconds); 1111 | } 1112 | 1113 | /** 1114 | * Set Url 1115 | * 1116 | * @access public 1117 | * @param $url 1118 | * @param $data 1119 | */ 1120 | public function setUrl($url, $data = array()) 1121 | { 1122 | $this->baseUrl = $url; 1123 | $this->url = $this->buildURL($url, $data); 1124 | $this->setOpt(CURLOPT_URL, $this->url); 1125 | } 1126 | 1127 | /** 1128 | * Set User Agent 1129 | * 1130 | * @access public 1131 | * @param $user_agent 1132 | */ 1133 | public function setUserAgent($user_agent) 1134 | { 1135 | $this->setOpt(CURLOPT_USERAGENT, $user_agent); 1136 | } 1137 | 1138 | /** 1139 | * Success 1140 | * 1141 | * @access public 1142 | * @param $callback 1143 | */ 1144 | public function success($callback) 1145 | { 1146 | $this->successFunction = $callback; 1147 | } 1148 | 1149 | /** 1150 | * Unset Header 1151 | * 1152 | * Remove extra header previously set using Curl::setHeader(). 1153 | * 1154 | * @access public 1155 | * @param $key 1156 | */ 1157 | public function unsetHeader($key) 1158 | { 1159 | unset($this->headers[$key]); 1160 | $headers = array(); 1161 | foreach ($this->headers as $key => $value) { 1162 | $headers[] = $key . ': ' . $value; 1163 | } 1164 | $this->setOpt(CURLOPT_HTTPHEADER, $headers); 1165 | } 1166 | 1167 | /** 1168 | * Remove Header 1169 | * 1170 | * Remove an internal header from the request. 1171 | * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');. 1172 | * 1173 | * @access public 1174 | * @param $key 1175 | */ 1176 | public function removeHeader($key) 1177 | { 1178 | $this->setHeader($key, ''); 1179 | } 1180 | 1181 | /** 1182 | * Verbose 1183 | * 1184 | * @access public 1185 | * @param bool $on 1186 | * @param resource $output 1187 | */ 1188 | public function verbose($on = true, $output = STDERR) 1189 | { 1190 | // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side 1191 | // effect of causing Curl::requestHeaders to be empty. 1192 | if ($on) { 1193 | $this->setOpt(CURLINFO_HEADER_OUT, false); 1194 | } 1195 | $this->setOpt(CURLOPT_VERBOSE, $on); 1196 | $this->setOpt(CURLOPT_STDERR, $output); 1197 | } 1198 | 1199 | /** 1200 | * Destruct 1201 | * 1202 | * @access public 1203 | */ 1204 | public function __destruct() 1205 | { 1206 | $this->close(); 1207 | } 1208 | 1209 | public function __get($name) 1210 | { 1211 | $return = null; 1212 | if (in_array($name, self::$deferredProperties) && is_callable(array($this, $getter = '__get_' . $name))) { 1213 | $return = $this->$name = $this->$getter(); 1214 | } 1215 | return $return; 1216 | } 1217 | 1218 | /** 1219 | * Get Effective Url 1220 | * 1221 | * @access private 1222 | */ 1223 | private function __get_effectiveUrl() 1224 | { 1225 | return $this->getInfo(CURLINFO_EFFECTIVE_URL); 1226 | } 1227 | 1228 | /** 1229 | * Get RFC 2616 1230 | * 1231 | * @access private 1232 | */ 1233 | private function __get_rfc2616() 1234 | { 1235 | return array_fill_keys(self::$RFC2616, true); 1236 | } 1237 | 1238 | /** 1239 | * Get RFC 6265 1240 | * 1241 | * @access private 1242 | */ 1243 | private function __get_rfc6265() 1244 | { 1245 | return array_fill_keys(self::$RFC6265, true); 1246 | } 1247 | 1248 | /** 1249 | * Get Total Time 1250 | * 1251 | * @access private 1252 | */ 1253 | private function __get_totalTime() 1254 | { 1255 | return $this->getInfo(CURLINFO_TOTAL_TIME); 1256 | } 1257 | 1258 | /** 1259 | * Build Url 1260 | * 1261 | * @access private 1262 | * @param $url 1263 | * @param $data 1264 | * 1265 | * @return string 1266 | */ 1267 | private function buildURL($url, $data = array()) 1268 | { 1269 | return $url . (empty($data) ? '' : '?' . http_build_query($data, '', '&')); 1270 | } 1271 | 1272 | /** 1273 | * Parse Headers 1274 | * 1275 | * @access private 1276 | * @param $raw_headers 1277 | * 1278 | * @return array 1279 | */ 1280 | private function parseHeaders($raw_headers) 1281 | { 1282 | $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY); 1283 | $http_headers = new CaseInsensitiveArray(); 1284 | 1285 | $raw_headers_count = count($raw_headers); 1286 | for ($i = 1; $i < $raw_headers_count; $i++) { 1287 | list($key, $value) = explode(':', $raw_headers[$i], 2); 1288 | $key = trim($key); 1289 | $value = trim($value); 1290 | // Use isset() as array_key_exists() and ArrayAccess are not compatible. 1291 | if (isset($http_headers[$key])) { 1292 | $http_headers[$key] .= ',' . $value; 1293 | } else { 1294 | $http_headers[$key] = $value; 1295 | } 1296 | } 1297 | 1298 | return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers); 1299 | } 1300 | 1301 | /** 1302 | * Parse Request Headers 1303 | * 1304 | * @access private 1305 | * @param $raw_headers 1306 | * 1307 | * @return array 1308 | */ 1309 | private function parseRequestHeaders($raw_headers) 1310 | { 1311 | $request_headers = new CaseInsensitiveArray(); 1312 | list($first_line, $headers) = $this->parseHeaders($raw_headers); 1313 | $request_headers['Request-Line'] = $first_line; 1314 | foreach ($headers as $key => $value) { 1315 | $request_headers[$key] = $value; 1316 | } 1317 | return $request_headers; 1318 | } 1319 | 1320 | /** 1321 | * Parse Response 1322 | * 1323 | * @access private 1324 | * @param $response_headers 1325 | * @param $raw_response 1326 | * 1327 | * @return mixed 1328 | * Provided the content-type is determined to be json or xml: 1329 | * Returns stdClass object when the default json decoder is used and the content-type is json. 1330 | * Returns SimpleXMLElement object when the default xml decoder is used and the content-type is xml. 1331 | */ 1332 | private function parseResponse($response_headers, $raw_response) 1333 | { 1334 | $response = $raw_response; 1335 | if (isset($response_headers['Content-Type'])) { 1336 | if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) { 1337 | $json_decoder = $this->jsonDecoder; 1338 | if (is_callable($json_decoder)) { 1339 | $response = $json_decoder($response); 1340 | } 1341 | } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) { 1342 | $xml_decoder = $this->xmlDecoder; 1343 | if (is_callable($xml_decoder)) { 1344 | $response = $xml_decoder($response); 1345 | } 1346 | } else { 1347 | $decoder = $this->defaultDecoder; 1348 | if (is_callable($decoder)) { 1349 | $response = $decoder($response); 1350 | } 1351 | } 1352 | } 1353 | 1354 | return $response; 1355 | } 1356 | 1357 | /** 1358 | * Parse Response Headers 1359 | * 1360 | * @access private 1361 | * @param $raw_response_headers 1362 | * 1363 | * @return array 1364 | */ 1365 | private function parseResponseHeaders($raw_response_headers) 1366 | { 1367 | $response_header_array = explode("\r\n\r\n", $raw_response_headers); 1368 | $response_header = ''; 1369 | for ($i = count($response_header_array) - 1; $i >= 0; $i--) { 1370 | if (stripos($response_header_array[$i], 'HTTP/') === 0) { 1371 | $response_header = $response_header_array[$i]; 1372 | break; 1373 | } 1374 | } 1375 | 1376 | $response_headers = new CaseInsensitiveArray(); 1377 | list($first_line, $headers) = $this->parseHeaders($response_header); 1378 | $response_headers['Status-Line'] = $first_line; 1379 | foreach ($headers as $key => $value) { 1380 | $response_headers[$key] = $value; 1381 | } 1382 | return $response_headers; 1383 | } 1384 | } 1385 | -------------------------------------------------------------------------------- /modules/servers/mailcow/lib/Curl/MultiCurl.php: -------------------------------------------------------------------------------- 1 | multiCurl = curl_multi_init(); 37 | $this->headers = new CaseInsensitiveArray(); 38 | $this->setUrl($base_url); 39 | } 40 | 41 | /** 42 | * Add Delete 43 | * 44 | * @access public 45 | * @param $url 46 | * @param $query_parameters 47 | * @param $data 48 | * 49 | * @return object 50 | */ 51 | public function addDelete($url, $query_parameters = array(), $data = array()) 52 | { 53 | if (is_array($url)) { 54 | $data = $query_parameters; 55 | $query_parameters = $url; 56 | $url = $this->baseUrl; 57 | } 58 | $curl = new Curl(); 59 | $curl->setUrl($url, $query_parameters); 60 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); 61 | $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); 62 | $this->queueHandle($curl); 63 | return $curl; 64 | } 65 | 66 | /** 67 | * Add Download 68 | * 69 | * @access public 70 | * @param $url 71 | * @param $mixed_filename 72 | * 73 | * @return object 74 | */ 75 | public function addDownload($url, $mixed_filename) 76 | { 77 | $curl = new Curl(); 78 | $curl->setUrl($url); 79 | 80 | // Use tmpfile() or php://temp to avoid "Too many open files" error. 81 | if (is_callable($mixed_filename)) { 82 | $callback = $mixed_filename; 83 | $curl->downloadCompleteFunction = $callback; 84 | $curl->fileHandle = tmpfile(); 85 | } else { 86 | $filename = $mixed_filename; 87 | $curl->downloadCompleteFunction = function ($instance, $fh) use ($filename) { 88 | file_put_contents($filename, stream_get_contents($fh)); 89 | }; 90 | $curl->fileHandle = fopen('php://temp', 'wb'); 91 | } 92 | 93 | $curl->setOpt(CURLOPT_FILE, $curl->fileHandle); 94 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); 95 | $curl->setOpt(CURLOPT_HTTPGET, true); 96 | $this->queueHandle($curl); 97 | return $curl; 98 | } 99 | 100 | /** 101 | * Add Get 102 | * 103 | * @access public 104 | * @param $url 105 | * @param $data 106 | * 107 | * @return object 108 | */ 109 | public function addGet($url, $data = array()) 110 | { 111 | if (is_array($url)) { 112 | $data = $url; 113 | $url = $this->baseUrl; 114 | } 115 | $curl = new Curl(); 116 | $curl->setUrl($url, $data); 117 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); 118 | $curl->setOpt(CURLOPT_HTTPGET, true); 119 | $this->queueHandle($curl); 120 | return $curl; 121 | } 122 | 123 | /** 124 | * Add Head 125 | * 126 | * @access public 127 | * @param $url 128 | * @param $data 129 | * 130 | * @return object 131 | */ 132 | public function addHead($url, $data = array()) 133 | { 134 | if (is_array($url)) { 135 | $data = $url; 136 | $url = $this->baseUrl; 137 | } 138 | $curl = new Curl(); 139 | $curl->setUrl($url, $data); 140 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); 141 | $curl->setOpt(CURLOPT_NOBODY, true); 142 | $this->queueHandle($curl); 143 | return $curl; 144 | } 145 | 146 | /** 147 | * Add Options 148 | * 149 | * @access public 150 | * @param $url 151 | * @param $data 152 | * 153 | * @return object 154 | */ 155 | public function addOptions($url, $data = array()) 156 | { 157 | if (is_array($url)) { 158 | $data = $url; 159 | $url = $this->baseUrl; 160 | } 161 | $curl = new Curl(); 162 | $curl->setUrl($url, $data); 163 | $curl->removeHeader('Content-Length'); 164 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); 165 | $this->queueHandle($curl); 166 | return $curl; 167 | } 168 | 169 | /** 170 | * Add Patch 171 | * 172 | * @access public 173 | * @param $url 174 | * @param $data 175 | * 176 | * @return object 177 | */ 178 | public function addPatch($url, $data = array()) 179 | { 180 | if (is_array($url)) { 181 | $data = $url; 182 | $url = $this->baseUrl; 183 | } 184 | $curl = new Curl(); 185 | $curl->setUrl($url); 186 | $curl->removeHeader('Content-Length'); 187 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); 188 | $curl->setOpt(CURLOPT_POSTFIELDS, $data); 189 | $this->queueHandle($curl); 190 | return $curl; 191 | } 192 | 193 | /** 194 | * Add Post 195 | * 196 | * @access public 197 | * @param $url 198 | * @param $data 199 | * @param $follow_303_with_post 200 | * If true, will cause 303 redirections to be followed using GET requests (default: false). 201 | * Note: Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. 202 | * 203 | * @return object 204 | */ 205 | public function addPost($url, $data = array(), $follow_303_with_post = false) 206 | { 207 | if (is_array($url)) { 208 | $follow_303_with_post = (bool)$data; 209 | $data = $url; 210 | $url = $this->baseUrl; 211 | } 212 | 213 | $curl = new Curl(); 214 | 215 | if (is_array($data) && empty($data)) { 216 | $curl->removeHeader('Content-Length'); 217 | } 218 | 219 | $curl->setUrl($url); 220 | 221 | /* 222 | * For post-redirect-get requests, the CURLOPT_CUSTOMREQUEST option must not 223 | * be set, otherwise cURL will perform POST requests for redirections. 224 | */ 225 | if (!$follow_303_with_post) { 226 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); 227 | } 228 | 229 | $curl->setOpt(CURLOPT_POST, true); 230 | $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); 231 | $this->queueHandle($curl); 232 | return $curl; 233 | } 234 | 235 | /** 236 | * Add Put 237 | * 238 | * @access public 239 | * @param $url 240 | * @param $data 241 | * 242 | * @return object 243 | */ 244 | public function addPut($url, $data = array()) 245 | { 246 | if (is_array($url)) { 247 | $data = $url; 248 | $url = $this->baseUrl; 249 | } 250 | $curl = new Curl(); 251 | $curl->setUrl($url); 252 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); 253 | $put_data = $curl->buildPostData($data); 254 | if (is_string($put_data)) { 255 | $curl->setHeader('Content-Length', strlen($put_data)); 256 | } 257 | $curl->setOpt(CURLOPT_POSTFIELDS, $put_data); 258 | $this->queueHandle($curl); 259 | return $curl; 260 | } 261 | 262 | /** 263 | * Add Search 264 | * 265 | * @access public 266 | * @param $url 267 | * @param $data 268 | * 269 | * @return object 270 | */ 271 | public function addSearch($url, $data = array()) 272 | { 273 | if (is_array($url)) { 274 | $data = $url; 275 | $url = $this->baseUrl; 276 | } 277 | $curl = new Curl(); 278 | $curl->setUrl($url); 279 | $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); 280 | $put_data = $curl->buildPostData($data); 281 | if (is_string($put_data)) { 282 | $curl->setHeader('Content-Length', strlen($put_data)); 283 | } 284 | $curl->setOpt(CURLOPT_POSTFIELDS, $put_data); 285 | $this->queueHandle($curl); 286 | return $curl; 287 | } 288 | 289 | /** 290 | * Add Curl 291 | * 292 | * Add a Curl instance to the handle queue. 293 | * 294 | * @access public 295 | * @param $curl 296 | * 297 | * @return object 298 | */ 299 | public function addCurl(Curl $curl) 300 | { 301 | $this->queueHandle($curl); 302 | return $curl; 303 | } 304 | 305 | /** 306 | * Before Send 307 | * 308 | * @access public 309 | * @param $callback 310 | */ 311 | public function beforeSend($callback) 312 | { 313 | $this->beforeSendFunction = $callback; 314 | } 315 | 316 | /** 317 | * Close 318 | * 319 | * @access public 320 | */ 321 | public function close() 322 | { 323 | foreach ($this->curls as $curl) { 324 | $curl->close(); 325 | } 326 | 327 | if (is_resource($this->multiCurl)) { 328 | curl_multi_close($this->multiCurl); 329 | } 330 | } 331 | 332 | /** 333 | * Complete 334 | * 335 | * @access public 336 | * @param $callback 337 | */ 338 | public function complete($callback) 339 | { 340 | $this->completeFunction = $callback; 341 | } 342 | 343 | /** 344 | * Error 345 | * 346 | * @access public 347 | * @param $callback 348 | */ 349 | public function error($callback) 350 | { 351 | $this->errorFunction = $callback; 352 | } 353 | 354 | /** 355 | * Get Opt 356 | * 357 | * @access public 358 | * @param $option 359 | * 360 | * @return mixed 361 | */ 362 | public function getOpt($option) 363 | { 364 | return isset($this->options[$option]) ? $this->options[$option] : null; 365 | } 366 | 367 | /** 368 | * Set Basic Authentication 369 | * 370 | * @access public 371 | * @param $username 372 | * @param $password 373 | */ 374 | public function setBasicAuthentication($username, $password = '') 375 | { 376 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 377 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 378 | } 379 | 380 | /** 381 | * Set Concurrency 382 | * 383 | * @access public 384 | * @param $concurrency 385 | */ 386 | public function setConcurrency($concurrency) 387 | { 388 | $this->concurrency = $concurrency; 389 | } 390 | 391 | /** 392 | * Set Digest Authentication 393 | * 394 | * @access public 395 | * @param $username 396 | * @param $password 397 | */ 398 | public function setDigestAuthentication($username, $password = '') 399 | { 400 | $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); 401 | $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); 402 | } 403 | 404 | /** 405 | * Set Cookie 406 | * 407 | * @access public 408 | * @param $key 409 | * @param $value 410 | */ 411 | public function setCookie($key, $value) 412 | { 413 | $this->cookies[$key] = $value; 414 | } 415 | 416 | /** 417 | * Set Cookies 418 | * 419 | * @access public 420 | * @param $cookies 421 | */ 422 | public function setCookies($cookies) 423 | { 424 | foreach ($cookies as $key => $value) { 425 | $this->cookies[$key] = $value; 426 | } 427 | } 428 | 429 | /** 430 | * Set Port 431 | * 432 | * @access public 433 | * @param $port 434 | */ 435 | public function setPort($port) 436 | { 437 | $this->setOpt(CURLOPT_PORT, intval($port)); 438 | } 439 | 440 | /** 441 | * Set Connect Timeout 442 | * 443 | * @access public 444 | * @param $seconds 445 | */ 446 | public function setConnectTimeout($seconds) 447 | { 448 | $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds); 449 | } 450 | 451 | /** 452 | * Set Cookie String 453 | * 454 | * @access public 455 | * @param $string 456 | */ 457 | public function setCookieString($string) 458 | { 459 | $this->setOpt(CURLOPT_COOKIE, $string); 460 | } 461 | 462 | /** 463 | * Set Cookie File 464 | * 465 | * @access public 466 | * @param $cookie_file 467 | */ 468 | public function setCookieFile($cookie_file) 469 | { 470 | $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); 471 | } 472 | 473 | /** 474 | * Set Cookie Jar 475 | * 476 | * @access public 477 | * @param $cookie_jar 478 | */ 479 | public function setCookieJar($cookie_jar) 480 | { 481 | $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); 482 | } 483 | 484 | /** 485 | * Set Header 486 | * 487 | * Add extra header to include in the request. 488 | * 489 | * @access public 490 | * @param $key 491 | * @param $value 492 | */ 493 | public function setHeader($key, $value) 494 | { 495 | $this->headers[$key] = $value; 496 | } 497 | 498 | /** 499 | * Set Headers 500 | * 501 | * Add extra headers to include in the request. 502 | * 503 | * @access public 504 | * @param $headers 505 | */ 506 | public function setHeaders($headers) 507 | { 508 | foreach ($headers as $key => $value) { 509 | $this->headers[$key] = $value; 510 | } 511 | } 512 | 513 | /** 514 | * Set JSON Decoder 515 | * 516 | * @access public 517 | * @param $function 518 | */ 519 | public function setJsonDecoder($function) 520 | { 521 | if (is_callable($function)) { 522 | $this->jsonDecoder = $function; 523 | } 524 | } 525 | 526 | /** 527 | * Set XML Decoder 528 | * 529 | * @access public 530 | * @param $function 531 | */ 532 | public function setXmlDecoder($function) 533 | { 534 | if (is_callable($function)) { 535 | $this->xmlDecoder = $function; 536 | } 537 | } 538 | 539 | /** 540 | * Set Opt 541 | * 542 | * @access public 543 | * @param $option 544 | * @param $value 545 | */ 546 | public function setOpt($option, $value) 547 | { 548 | $this->options[$option] = $value; 549 | } 550 | 551 | /** 552 | * Set Opts 553 | * 554 | * @access public 555 | * @param $options 556 | */ 557 | public function setOpts($options) 558 | { 559 | foreach ($options as $option => $value) { 560 | $this->setOpt($option, $value); 561 | } 562 | } 563 | 564 | /** 565 | * Set Referer 566 | * 567 | * @access public 568 | * @param $referer 569 | */ 570 | public function setReferer($referer) 571 | { 572 | $this->setReferrer($referer); 573 | } 574 | 575 | /** 576 | * Set Referrer 577 | * 578 | * @access public 579 | * @param $referrer 580 | */ 581 | public function setReferrer($referrer) 582 | { 583 | $this->setOpt(CURLOPT_REFERER, $referrer); 584 | } 585 | 586 | /** 587 | * Set Timeout 588 | * 589 | * @access public 590 | * @param $seconds 591 | */ 592 | public function setTimeout($seconds) 593 | { 594 | $this->setOpt(CURLOPT_TIMEOUT, $seconds); 595 | } 596 | 597 | /** 598 | * Set Url 599 | * 600 | * @access public 601 | * @param $url 602 | */ 603 | public function setUrl($url) 604 | { 605 | $this->baseUrl = $url; 606 | } 607 | 608 | /** 609 | * Set User Agent 610 | * 611 | * @access public 612 | * @param $user_agent 613 | */ 614 | public function setUserAgent($user_agent) 615 | { 616 | $this->setOpt(CURLOPT_USERAGENT, $user_agent); 617 | } 618 | 619 | /** 620 | * Start 621 | * 622 | * @access public 623 | */ 624 | public function start() 625 | { 626 | if ($this->isStarted) { 627 | return; 628 | } 629 | 630 | $this->isStarted = true; 631 | 632 | $concurrency = $this->concurrency; 633 | if ($concurrency > count($this->curls)) { 634 | $concurrency = count($this->curls); 635 | } 636 | 637 | for ($i = 0; $i < $concurrency; $i++) { 638 | $this->initHandle(array_shift($this->curls)); 639 | } 640 | 641 | do { 642 | curl_multi_select($this->multiCurl); 643 | curl_multi_exec($this->multiCurl, $active); 644 | 645 | while (!($info_array = curl_multi_info_read($this->multiCurl)) === false) { 646 | if ($info_array['msg'] === CURLMSG_DONE) { 647 | foreach ($this->activeCurls as $key => $ch) { 648 | if ($ch->curl === $info_array['handle']) { 649 | // Set the error code for multi handles using the "result" key in the array returned by 650 | // curl_multi_info_read(). Using curl_errno() on a multi handle will incorrectly return 0 651 | // for errors. 652 | $ch->curlErrorCode = $info_array['result']; 653 | $ch->exec($ch->curl); 654 | 655 | unset($this->activeCurls[$key]); 656 | 657 | // Start a new request before removing the handle of the completed one. 658 | if (count($this->curls) >= 1) { 659 | $this->initHandle(array_shift($this->curls)); 660 | } 661 | curl_multi_remove_handle($this->multiCurl, $ch->curl); 662 | 663 | break; 664 | } 665 | } 666 | } 667 | } 668 | 669 | if (!$active) { 670 | $active = count($this->activeCurls); 671 | } 672 | } while ($active > 0); 673 | 674 | $this->isStarted = false; 675 | } 676 | 677 | /** 678 | * Success 679 | * 680 | * @access public 681 | * @param $callback 682 | */ 683 | public function success($callback) 684 | { 685 | $this->successFunction = $callback; 686 | } 687 | 688 | /** 689 | * Unset Header 690 | * 691 | * Remove extra header previously set using Curl::setHeader(). 692 | * 693 | * @access public 694 | * @param $key 695 | */ 696 | public function unsetHeader($key) 697 | { 698 | unset($this->headers[$key]); 699 | } 700 | 701 | /** 702 | * Remove Header 703 | * 704 | * Remove an internal header from the request. 705 | * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');. 706 | * 707 | * @access public 708 | * @param $key 709 | */ 710 | public function removeHeader($key) 711 | { 712 | $this->setHeader($key, ''); 713 | } 714 | 715 | /** 716 | * Verbose 717 | * 718 | * @access public 719 | * @param bool $on 720 | * @param resource $output 721 | */ 722 | public function verbose($on = true, $output = STDERR) 723 | { 724 | // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side 725 | // effect of causing Curl::requestHeaders to be empty. 726 | if ($on) { 727 | $this->setOpt(CURLINFO_HEADER_OUT, false); 728 | } 729 | $this->setOpt(CURLOPT_VERBOSE, $on); 730 | $this->setOpt(CURLOPT_STDERR, $output); 731 | } 732 | 733 | /** 734 | * Destruct 735 | * 736 | * @access public 737 | */ 738 | public function __destruct() 739 | { 740 | $this->close(); 741 | } 742 | 743 | /** 744 | * Queue Handle 745 | * 746 | * @access private 747 | * @param $curl 748 | */ 749 | private function queueHandle($curl) 750 | { 751 | // Use sequential ids to allow for ordered post processing. 752 | $curl->id = $this->nextCurlId++; 753 | $this->curls[$curl->id] = $curl; 754 | } 755 | 756 | /** 757 | * Init Handle 758 | * 759 | * @access private 760 | * @param $curl 761 | * @throws \ErrorException 762 | */ 763 | private function initHandle($curl) 764 | { 765 | // Set callbacks if not already individually set. 766 | if ($curl->beforeSendFunction === null) { 767 | $curl->beforeSend($this->beforeSendFunction); 768 | } 769 | if ($curl->successFunction === null) { 770 | $curl->success($this->successFunction); 771 | } 772 | if ($curl->errorFunction === null) { 773 | $curl->error($this->errorFunction); 774 | } 775 | if ($curl->completeFunction === null) { 776 | $curl->complete($this->completeFunction); 777 | } 778 | 779 | $curl->setOpts($this->options); 780 | $curl->setHeaders($this->headers); 781 | 782 | foreach ($this->cookies as $key => $value) { 783 | $curl->setCookie($key, $value); 784 | } 785 | 786 | $curl->setJsonDecoder($this->jsonDecoder); 787 | $curl->setXmlDecoder($this->xmlDecoder); 788 | 789 | $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl); 790 | if (!($curlm_error_code === CURLM_OK)) { 791 | throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); 792 | } 793 | 794 | $this->activeCurls[$curl->id] = $curl; 795 | $curl->call($curl->beforeSendFunction); 796 | } 797 | } 798 | -------------------------------------------------------------------------------- /modules/servers/mailcow/lib/MailcowAPI.php: -------------------------------------------------------------------------------- 1 | MAILBOXQUOTA = $params['configoption1']; 31 | } 32 | 33 | $this->baseurl = 'https://' . $params['serverhostname']; 34 | 35 | $this->curl = new Curl(); 36 | $this->curl->setOpt(CURLOPT_FOLLOWLOCATION, true); 37 | $this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, false); //ignore certificate issues 38 | $this->curl->setCookieFile(''); 39 | $this->curl->setCookieJar(dirname(__FILE__) . '/cookiejar.txt'); 40 | 41 | $data = array( 42 | 'login_user' => $params['serverusername'], 43 | 'pass_user' => html_entity_decode($params['serverpassword']), 44 | ); 45 | 46 | //Create session 47 | $this->curl->post($this->baseurl, $data); 48 | if ($this->curl->error) { 49 | throw new \Exception('Error creating session: ' . $this->curl->errorMessage); 50 | } 51 | else{ 52 | $this->csrf_token = $this->getToken($this->error_checking('init', $data)); 53 | //$this->cookie = $this->curl->getCookie('PHPSESSID'); //May not be essential 54 | $this->curl->setHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); 55 | } 56 | 57 | } 58 | 59 | /** 60 | * Domain functions 61 | */ 62 | 63 | public function addDomain($params){ 64 | 65 | return $this->_manageDomain($params['domain'], $params['configoptions'], 'create'); 66 | 67 | } 68 | 69 | public function editDomain($params){ 70 | 71 | return $this->_manageDomain($params['domain'], $params['configoptions'], 'edit'); 72 | 73 | } 74 | 75 | public function disableDomain($params){ 76 | 77 | return $this->_manageDomain($params['domain'], $params['configoptions'], 'disable'); 78 | 79 | } 80 | 81 | public function activateDomain($params){ 82 | 83 | return $this->_manageDomain($params['domain'], $params['configoptions'], 'activate'); 84 | 85 | } 86 | 87 | public function removeDomain($params){ 88 | 89 | return $this->_manageDomain($params['domain'], $params['configoptions'], 'remove'); 90 | 91 | } 92 | 93 | private function _manageDomain($domain, $product_config, $action){ 94 | 95 | $attr = array( 96 | 'description' => '', 97 | 'aliases' => $this->aliases, 98 | //'backupmx' => 0, 99 | //'relay_all_recipients' => 0, 100 | ); 101 | 102 | /** Number of Mailbox Based Limits **/ 103 | if ( !empty($product_config['Email Accounts']) ){ 104 | $attr['mailboxes'] = $product_config['Email Accounts']; 105 | $attr['maxquota'] = $this->MAILBOXQUOTA; //per mailbox 106 | $attr['quota'] = $this->MAILBOXQUOTA * $product_config['Email Accounts']; //for total domain 107 | } 108 | 109 | /** Domain Storage Based Limits **/ 110 | if ( !empty($product_config['Disk Space']) ){ 111 | $attr['mailboxes'] = $this->UNL_MAILBOXES; 112 | $attr['maxquota'] = $product_config['Disk Space']; //per mailbox 113 | $attr['quota'] = $product_config['Disk Space']; //for total domain 114 | } 115 | 116 | $uri = '/api/v1/edit/domain'; 117 | 118 | switch ($action){ 119 | 120 | case 'create': 121 | $uri = '/api/v1/add/domain'; 122 | $attr['domain'] = $domain; 123 | $attr['active'] = 1; 124 | break; 125 | case 'edit': 126 | case 'disable': 127 | $data['items'] = json_encode(array($domain)); 128 | $attr['active'] = 0; 129 | break; 130 | case 'activate': 131 | $data['items'] = json_encode(array($domain)); 132 | $attr['active'] = array('0','1'); 133 | break; 134 | case 'remove': 135 | $uri = '/api/v1/delete/domain'; 136 | $data['items'] = json_encode(array($domain)); 137 | break; 138 | 139 | } 140 | /* For some reason they include it twice. Once as part of attr and once outside */ 141 | $attr['csrf_token'] = $this->csrf_token; 142 | $data['csrf_token'] = $this->csrf_token; 143 | 144 | $data['attr'] = json_encode($attr); 145 | 146 | $this->curl->post($this->baseurl . $uri, $data); 147 | 148 | try{ 149 | $result = $this->error_checking($uri, $data); 150 | } 151 | catch (Exception $e) { 152 | return $e->getMessage(); 153 | } 154 | 155 | if ( $action == 'create' && empty($this->curl->error) ){ 156 | $this->_restartSogo(); //on successful creation, restart SOGo 157 | return $this->curl->response; //can't use errorCheck function after running _restartSogo() as errors are shown differently. 158 | } 159 | else{ 160 | return $result; 161 | } 162 | 163 | } 164 | 165 | 166 | /** 167 | * Domain Administrator Functions 168 | */ 169 | 170 | public function addDomainAdmin($params){ 171 | 172 | return $this->_manageDomainAdmin($params['domain'], $params['username'], $params['password'], 'create'); 173 | 174 | } 175 | 176 | public function editDomainAdmin($domain, $username){ 177 | 178 | return $this->_manageDomainAdmin($domain, $username, null, 'edit'); 179 | 180 | } 181 | 182 | public function disableDomainAdmin($domain, $username){ 183 | 184 | return $this->_manageDomainAdmin($domain, $username, null, 'disable'); 185 | 186 | } 187 | 188 | public function activateDomainAdmin($domain, $username){ 189 | 190 | return $this->_manageDomainAdmin($domain, $username, null, 'activate'); 191 | 192 | } 193 | 194 | public function changePasswordDomainAdmin($domain, $username, $password){ 195 | 196 | return $this->_manageDomainAdmin($domain, $username, $password, 'changepass'); 197 | 198 | } 199 | 200 | public function removeDomainAdmin($params){ 201 | 202 | return $this->_manageDomainAdmin($params['domain'], $params['username'], null, 'remove'); 203 | 204 | } 205 | 206 | private function _manageDomainAdmin($domain, $username, $password, $action){ 207 | 208 | $uri = '/api/v1/edit/domain-admin'; //default 209 | 210 | switch ($action){ 211 | 212 | case 'create': 213 | $uri = '/api/v1/add/domain-admin'; 214 | $attr['domains'] = $domain; 215 | $attr['username'] = $username; 216 | $attr['password'] = $password; 217 | $attr['password2'] = $password; 218 | $attr['active'] = 1; 219 | break; 220 | case 'edit': 221 | case 'changepass': 222 | $data['items'] = json_encode(array($username)); 223 | $attr['domains'] = $domain; 224 | $attr['username_new'] = $username; 225 | $attr['password'] = $password; 226 | $attr['password2'] = $password; 227 | $attr['active'] = 1; 228 | break; 229 | case 'disable': 230 | $data['items'] = array($username); 231 | $attr['active'] = 0; 232 | break; 233 | case 'activate': 234 | $data['items'] = json_encode(array($username)); 235 | $attr['active'] = array('0','1'); 236 | $attr['username_new'] = $username; 237 | $attr['domains'] = $domain; 238 | $attr['password'] = $password; 239 | $attr['password2'] = $password; 240 | break; 241 | case 'remove': 242 | $data['items'] = json_encode(array($username)); 243 | $uri = '/api/v1/delete/domain-admin'; 244 | break; 245 | 246 | } 247 | 248 | $attr['csrf_token'] = $this->csrf_token; 249 | $data['csrf_token'] = $this->csrf_token; 250 | $data['attr'] = json_encode($attr); 251 | 252 | $this->curl->post($this->baseurl . $uri, $data); 253 | 254 | try{ 255 | $result = $this->error_checking($uri, $data); 256 | } 257 | catch (Exception $e) { 258 | return $e->getMessage(); 259 | } 260 | 261 | return $result; 262 | 263 | } 264 | 265 | public function removeAllMailboxes($domain){ 266 | 267 | if ( isset($domain) && !empty($domain) ){ 268 | 269 | $mailboxes = $this->_getMailboxes(); 270 | 271 | $mbaddrs = array(); 272 | foreach ($mailboxes as $mbinfo){ 273 | if ( $mbinfo->domain === $domain ){ 274 | array_push($mbaddrs, $mbinfo->username); 275 | } 276 | } 277 | 278 | if (!empty($mbaddrs)) $this->_removeMailboxes($mbaddrs); 279 | 280 | } 281 | else{ 282 | return "Error: Domain not provided."; 283 | } 284 | 285 | } 286 | 287 | public function removeAllResources($domain){ 288 | 289 | if ( isset($domain) && !empty($domain) ){ 290 | 291 | $resources_json = $this->_getResources(); 292 | 293 | $resources = array(); 294 | foreach ($resources_json as $rinfo){ 295 | if ( $rinfo->domain === $domain ){ 296 | array_push($resources, $rinfo->name); 297 | } 298 | } 299 | 300 | if (!empty($resources)) $this->_removeResources($resources); 301 | 302 | } 303 | else{ 304 | return "Error: Domain not provided."; 305 | } 306 | 307 | } 308 | 309 | // Expects array of $mailboxes 310 | private function _removeMailboxes($mailboxes){ 311 | 312 | $data = array( 313 | 'items' => json_encode($mailboxes), 314 | 'csrf_token' => $this->csrf_token, 315 | ); 316 | 317 | $uri = '/api/v1/delete/mailbox'; 318 | 319 | $this->curl->post($this->baseurl . $uri, $data); 320 | 321 | try{ 322 | $result = $this->error_checking($uri, $data); 323 | } 324 | catch (Exception $e) { 325 | return $e->getMessage(); 326 | } 327 | 328 | } 329 | 330 | private function _removeResources($resources){ 331 | 332 | $data = array( 333 | 'items' => json_encode($resources), 334 | 'csrf_token' => $this->csrf_token, 335 | ); 336 | 337 | $uri = '/api/v1/delete/resource'; 338 | 339 | $this->curl->post($this->baseurl . $uri, $data); 340 | 341 | try{ 342 | $result = $this->error_checking($uri, $data); 343 | } 344 | catch (Exception $e) { 345 | return $e->getMessage(); 346 | } 347 | 348 | } 349 | 350 | /* We don't actually use $domains */ 351 | public function getUsageStats($domains){ 352 | 353 | $usagedata = array(); 354 | 355 | foreach ($this->_getDomains() as $domain){ 356 | //Init disk usage to 0 and set quota to actual value. 357 | $usagedata[$domain->domain_name] = array( 358 | 'disklimit' => (float) ($domain->max_quota_for_domain / (1024*1024)), 359 | 'diskusage' => 0, 360 | ); 361 | } 362 | 363 | foreach ($this->_getMailboxes() as $mailbox){ 364 | //Increase disk usage for domain by this mailboxes' usage 365 | $usagedata[$mailbox->domain]['diskusage'] += (float) ($mailbox->quota_used / (1024*1024)); 366 | } 367 | 368 | //logActivity( print_r($usagedata, true) ); ///DEBUG 369 | 370 | return $usagedata; 371 | 372 | } 373 | 374 | private function _getDomains(){ 375 | 376 | $uri = '/api/v1/get/domain/all'; 377 | $data = array(); 378 | 379 | $this->curl->get( $this->baseurl . $uri, $data ); 380 | 381 | try{ 382 | $result = $this->error_checking($uri, $data); 383 | } 384 | catch (Exception $e) { 385 | return $e->getMessage(); 386 | } 387 | 388 | return $result; 389 | 390 | } 391 | 392 | private function _getMailboxes(){ 393 | 394 | $uri = '/api/v1/get/mailbox/all'; 395 | $data = array(); 396 | 397 | $this->curl->get( $this->baseurl . $uri, $data ); 398 | 399 | try{ 400 | $result = $this->error_checking($uri, $data); 401 | } 402 | catch (Exception $e) { 403 | return $e->getMessage(); 404 | } 405 | 406 | return $result; 407 | 408 | } 409 | 410 | private function _getResources(){ 411 | 412 | $uri = '/api/v1/get/resource/all'; 413 | $data = array(); 414 | 415 | $this->curl->get( $this->baseurl . $uri, $data ); 416 | 417 | try{ 418 | $result = $this->error_checking($uri, $data); 419 | } 420 | catch (Exception $e) { 421 | return $e->getMessage(); 422 | } 423 | 424 | return $result; 425 | 426 | } 427 | 428 | private function _restartSogo(){ 429 | 430 | $this->curl->get( $this->baseurl . '/inc/call_sogo_ctrl.php', array('ACTION' => 'stop') ); 431 | 432 | if ($this->curl->error) { 433 | 434 | return array( 435 | 'error' => $this->curl->errorCode, 436 | 'error_message' => $this->curl->errorMessage, 437 | ); 438 | 439 | } else { 440 | 441 | $this->curl->get( $this->baseurl . '/inc/call_sogo_ctrl.php', array('ACTION' => 'start') ); 442 | 443 | if ($this->curl->error) { 444 | 445 | return array( 446 | 'error' => $this->curl->errorCode, 447 | 'error_message' => $this->curl->errorMessage, 448 | ); 449 | 450 | } else { 451 | 452 | return $this->curl->response; 453 | 454 | } 455 | 456 | } 457 | 458 | } 459 | 460 | private function error_checking($action = null, $data_sent = null){ 461 | 462 | // first check for standard HTTP errors like 404 463 | if ($this->curl->error){ 464 | throw new \Exception($this->curl->errorCode . ': ' . $this->curl->errorMessage); 465 | } 466 | // then check for mailcow errors 467 | else { 468 | $json = $this->curl->response; 469 | 470 | if ($action !== 'init'){ //silence initial connection logging 471 | logModuleCall( 472 | 'mailcow', 473 | $action, 474 | print_r($data_sent, true), 475 | print_r($json, true), 476 | null 477 | ); 478 | } 479 | 480 | if ($json->type == "error" || $json->type == "danger"){ 481 | throw new \Exception($json->msg); 482 | } 483 | } 484 | 485 | return $this->curl->response; 486 | 487 | } 488 | 489 | private function getToken($html){ 490 | 491 | $token_pattern = "/var csrf_token = '([A-Za-z\d]+)';/sim"; 492 | 493 | if ( preg_match($token_pattern, $html, $matches) ){ 494 | return strip_tags( $matches[1] ); 495 | } 496 | 497 | } 498 | 499 | } /* Close MailCow_API class */ 500 | 501 | ?> -------------------------------------------------------------------------------- /modules/servers/mailcow/lib/README.md: -------------------------------------------------------------------------------- 1 | Include libraries and third party modules here. 2 | -------------------------------------------------------------------------------- /modules/servers/mailcow/mailcow.php: -------------------------------------------------------------------------------- 1 | 'MailCow', 32 | 'APIVersion' => '1.1', // Use API Version 1.1 33 | 'RequiresServer' => true, // Set true if module requires a server to work 34 | 'DefaultNonSSLPort' => '80', // Default Non-SSL Connection Port 35 | 'DefaultSSLPort' => '443', // Default SSL Connection Port 36 | 'ServiceSingleSignOnLabel' => 'Login to Panel as User', 37 | 'AdminSingleSignOnLabel' => 'Login to Panel as Admin', 38 | ); 39 | } 40 | 41 | /** 42 | * Define product configuration options. 43 | * 44 | * The values you return here define the configuration options that are 45 | * presented to a user when configuring a product for use with the module. These 46 | * values are then made available in all module function calls with the key name 47 | * configoptionX - with X being the index number of the field from 1 to 24. 48 | * 49 | * You can specify up to 24 parameters, with field types: 50 | * * text 51 | * * password 52 | * * yesno 53 | * * dropdown 54 | * * radio 55 | * * textarea 56 | * 57 | * Examples of each and their possible configuration parameters are provided in 58 | * this sample function. 59 | * 60 | * @return array 61 | */ 62 | function mailcow_ConfigOptions(){ 63 | 64 | return array( 65 | 'Default Mailbox Quota' => array( 66 | 'Type' => 'text', 67 | 'Size' => '25', 68 | 'Default' => '1024', 69 | 'Description' => 'When specifying per-account limits, this storage limit will be applied to each mailbox. Enter in megabytes', 70 | ) 71 | ); 72 | } 73 | 74 | /** 75 | * Provision a new instance of a product/service. 76 | * 77 | * Attempt to provision a new instance of a given product/service. This is 78 | * called any time provisioning is requested inside of WHMCS. Depending upon the 79 | * configuration, this can be any of: 80 | * * When a new order is placed 81 | * * When an invoice for a new order is paid 82 | * * Upon manual request by an admin user 83 | * 84 | * @param array $params common module parameters 85 | * 86 | * @see http://docs.whmcs.com/Provisioning_Module_SDK_Parameters 87 | * 88 | * @return string "success" or an error message 89 | */ 90 | function mailcow_CreateAccount(array $params) 91 | { 92 | 93 | try { 94 | 95 | $mailcow = new MailcowAPI($params); 96 | $result = $mailcow->addDomain($params); 97 | 98 | } catch (Exception $e) { 99 | 100 | return $e->getMessage(); 101 | } 102 | 103 | try { 104 | 105 | $mailcow = new MailcowAPI($params); 106 | $result = $mailcow->addDomainAdmin($params); 107 | 108 | } catch (Exception $e) { 109 | 110 | return $e->getMessage(); 111 | } 112 | 113 | return 'success'; 114 | } 115 | 116 | /** 117 | * Suspend an instance of a product/service. 118 | * 119 | * Called when a suspension is requested. This is invoked automatically by WHMCS 120 | * when a product becomes overdue on payment or can be called manually by admin 121 | * user. 122 | * 123 | * @param array $params common module parameters 124 | * 125 | * @see http://docs.whmcs.com/Provisioning_Module_SDK_Parameters 126 | * 127 | * @return string "success" or an error message 128 | */ 129 | 130 | function mailcow_SuspendAccount(array $params) 131 | { 132 | try { 133 | 134 | $mailcow = new MailcowAPI($params); 135 | $result = $mailcow->disableDomain($params); 136 | 137 | } catch (Exception $e) { 138 | 139 | return $e->getMessage(); 140 | } 141 | 142 | try { 143 | 144 | $mailcow = new MailcowAPI($params); 145 | $result = $mailcow->disableDomainAdmin($params['domain'], $params['username']); 146 | 147 | } catch (Exception $e) { 148 | 149 | return $e->getMessage(); 150 | } 151 | 152 | return 'success'; 153 | } 154 | 155 | /** 156 | * Un-suspend instance of a product/service. 157 | * 158 | * Called when an un-suspension is requested. This is invoked 159 | * automatically upon payment of an overdue invoice for a product, or 160 | * can be called manually by admin user. 161 | * 162 | * @param array $params common module parameters 163 | * 164 | * @see http://docs.whmcs.com/Provisioning_Module_SDK_Parameters 165 | * 166 | * @return string "success" or an error message 167 | */ 168 | 169 | function mailcow_UnsuspendAccount(array $params) 170 | { 171 | try { 172 | 173 | $mailcow = new MailcowAPI($params); 174 | $result = $mailcow->activateDomain($params); 175 | 176 | } catch (Exception $e) { 177 | 178 | return $e->getMessage(); 179 | } 180 | 181 | try { 182 | 183 | $mailcow = new MailcowAPI($params); 184 | $result = $mailcow->activateDomainAdmin($params['domain'], $params['username']); 185 | 186 | } catch (Exception $e) { 187 | 188 | return $e->getMessage(); 189 | } 190 | 191 | return 'success'; 192 | } 193 | 194 | /** 195 | * Terminate instance of a product/service. 196 | * 197 | * Called when a termination is requested. This can be invoked automatically for 198 | * overdue products if enabled, or requested manually by an admin user. 199 | * 200 | * @param array $params common module parameters 201 | * 202 | * @see http://docs.whmcs.com/Provisioning_Module_SDK_Parameters 203 | * 204 | * @return string "success" or an error message 205 | */ 206 | 207 | function mailcow_TerminateAccount(array $params) 208 | { 209 | try { 210 | 211 | //Remove Mailboxes 212 | $mailcow = new MailcowAPI($params); 213 | $result = $mailcow->removeAllMailboxes($params['domain']); 214 | 215 | //Remove Resources 216 | $mailcow = new MailcowAPI($params); 217 | $result = $mailcow->removeAllResources($params['domain']); 218 | 219 | //Remove Domain 220 | $mailcow = new MailcowAPI($params); 221 | $result = $mailcow->removeDomain($params); 222 | 223 | //Remove Domain Admin 224 | $mailcow = new MailcowAPI($params); 225 | $result = $mailcow->removeDomainAdmin($params); 226 | 227 | } catch (Exception $e) { 228 | 229 | return $e->getMessage(); 230 | } 231 | 232 | return 'success'; 233 | } 234 | 235 | /** 236 | * Change the password for an instance of a product/service. 237 | * 238 | * Called when a password change is requested. This can occur either due to a 239 | * client requesting it via the client area or an admin requesting it from the 240 | * admin side. 241 | * 242 | * This option is only available to client end users when the product is in an 243 | * active status. 244 | * 245 | * @param array $params common module parameters 246 | * 247 | * @see http://docs.whmcs.com/Provisioning_Module_SDK_Parameters 248 | * 249 | * @return string "success" or an error message 250 | */ 251 | 252 | function mailcow_ChangePassword(array $params) 253 | { 254 | try { 255 | 256 | $mailcow = new MailcowAPI($params); 257 | $result = $mailcow->changePasswordDomainAdmin($params['domain'], $params['username'], $params['password']); 258 | 259 | logModuleCall( 260 | 'mailcow', 261 | __FUNCTION__, 262 | print_r($params, true), 263 | print_r($result, true), 264 | null 265 | ); 266 | 267 | } catch (Exception $e) { 268 | // Record the error in WHMCS's module log. 269 | logModuleCall( 270 | 'mailcow', 271 | __FUNCTION__, 272 | print_r($params, true), 273 | $e->getMessage(), 274 | $e->getTraceAsString() 275 | ); 276 | 277 | return $e->getMessage(); 278 | } 279 | 280 | return 'success'; 281 | } 282 | 283 | /** 284 | * Upgrade or downgrade an instance of a product/service. 285 | * 286 | * Called to apply any change in product assignment or parameters. It 287 | * is called to provision upgrade or downgrade orders, as well as being 288 | * able to be invoked manually by an admin user. 289 | * 290 | * This same function is called for upgrades and downgrades of both 291 | * products and configurable options. 292 | * 293 | * @param array $params common module parameters 294 | * 295 | * @see http://docs.whmcs.com/Provisioning_Module_SDK_Parameters 296 | * 297 | * @return string "success" or an error message 298 | */ 299 | function mailcow_ChangePackage(array $params) 300 | { 301 | try { 302 | 303 | $mailcow = new MailcowAPI($params); 304 | $result = $mailcow->editDomain($params); 305 | 306 | logModuleCall( 307 | 'mailcow', 308 | __FUNCTION__, 309 | print_r($params, true), 310 | print_r($result, true), 311 | null 312 | ); 313 | 314 | } catch (Exception $e) { 315 | // Record the error in WHMCS's module log. 316 | logModuleCall( 317 | 'mailcow', 318 | __FUNCTION__, 319 | print_r($params, true), 320 | $e->getMessage(), 321 | $e->getTraceAsString() 322 | ); 323 | 324 | return $e->getMessage(); 325 | } 326 | 327 | return 'success'; 328 | } 329 | 330 | /** 331 | * Test connection with the given server parameters. 332 | * 333 | * Allows an admin user to verify that an API connection can be 334 | * successfully made with the given configuration parameters for a 335 | * server. 336 | * 337 | * When defined in a module, a Test Connection button will appear 338 | * alongside the Server Type dropdown when adding or editing an 339 | * existing server. 340 | * 341 | * @param array $params common module parameters 342 | * 343 | * @see http://docs.whmcs.com/Provisioning_Module_SDK_Parameters 344 | * 345 | * @return array 346 | */ 347 | function mailcow_TestConnection(array $params) 348 | { 349 | try { 350 | // Call the service's connection test function. 351 | 352 | $success = true; 353 | $errorMsg = ''; 354 | } catch (Exception $e) { 355 | // Record the error in WHMCS's module log. 356 | logModuleCall( 357 | 'mailcow', 358 | __FUNCTION__, 359 | print_r($params, true), 360 | $e->getMessage(), 361 | $e->getTraceAsString() 362 | ); 363 | 364 | $success = false; 365 | $errorMsg = $e->getMessage(); 366 | } 367 | 368 | return array( 369 | 'success' => $success, 370 | 'error' => $errorMsg, 371 | ); 372 | } 373 | 374 | 375 | /** 376 | * @param $params 377 | * @return string 378 | */ 379 | function mailcow_AdminLink(array $params) 380 | { 381 | $address = ($params['serverhostname']) ? $params['serverhostname'] : $params['serverip']; 382 | $secure = ($params["serversecure"]) ? 'https' : 'http'; 383 | if (empty($address)) { 384 | return ''; 385 | } 386 | 387 | $form = sprintf( 388 | '
' . 389 | '' . 390 | '' . 391 | '' . 392 | '
', 393 | $secure, 394 | Sanitize::encode($address), 395 | Sanitize::encode($params["serverusername"]), 396 | Sanitize::encode($params["serverpassword"]), 397 | 'Login to panel' 398 | ); 399 | 400 | return $form; 401 | } 402 | 403 | /** 404 | * @param $params 405 | * @return string 406 | */ 407 | function mailcow_ClientArea(array $params) { 408 | 409 | $address = ($params['serverhostname']) ? $params['serverhostname'] : $params['serverip']; 410 | $secure = ($params["serversecure"]) ? 'https' : 'http'; 411 | if (empty($address)) { 412 | return ''; 413 | } 414 | 415 | $form = sprintf( 416 | '
' . 417 | '' . 418 | '' . 419 | '' . 420 | '
', 421 | $secure, 422 | Sanitize::encode($address), 423 | Sanitize::encode($params["username"]), 424 | Sanitize::encode($params["password"]), 425 | 'Open Control Panel' 426 | ); 427 | 428 | return $form; 429 | 430 | } 431 | 432 | /** 433 | * Admin Area Client Login link 434 | */ 435 | function mailcow_LoginLink(array $params){ /** Not working Need to use JS to submit form **/ 436 | /* 437 | return " 440 | Login as User"; 441 | */ 442 | } 443 | 444 | 445 | /** 446 | * @param $params 447 | * @return string 448 | */ 449 | function mailcow_UsageUpdate($params) { 450 | 451 | $query = Capsule::table('tblhosting') 452 | ->where('server', $params["serverid"]); 453 | 454 | $domains = array(); 455 | /** @var stdClass $hosting */ 456 | foreach ($query->get() as $hosting) { 457 | $domains[] = $hosting->domain; 458 | } 459 | 460 | try { 461 | 462 | $mailcow = new MailcowAPI($params); 463 | $domainsUsage = $mailcow->getUsageStats($domains); 464 | 465 | logModuleCall( 466 | 'mailcow', 467 | __FUNCTION__, 468 | $params, 469 | print_r($domainsUsage, true), 470 | null 471 | ); 472 | 473 | } catch (Exception $e) { 474 | // Record the error in WHMCS's module log. 475 | logModuleCall( 476 | 'mailcow', 477 | __FUNCTION__, 478 | $params, 479 | $e->getMessage(), 480 | $e->getTraceAsString() 481 | ); 482 | 483 | return $e->getMessage(); 484 | } 485 | 486 | //logActivity(print_r($domainsUsage, true)); ////DEBUG 487 | 488 | foreach ( $domainsUsage as $domainName => $usage ) { 489 | 490 | Capsule::table('tblhosting') 491 | ->where('server', $params["serverid"]) 492 | ->where('domain', $domainName) 493 | ->update( 494 | array( 495 | "diskusage" => $usage['diskusage'], 496 | "disklimit" => $usage['disklimit'], 497 | //"bwusage" => $usage['bwusage'], 498 | //"bwlimit" => $usage['bwlimit'], 499 | "lastupdate" => Capsule::table('tblhosting')->raw('now()'), 500 | ) 501 | ); 502 | } 503 | 504 | return 'success'; 505 | } -------------------------------------------------------------------------------- /modules/servers/mailcow/templates/error.tpl: -------------------------------------------------------------------------------- 1 |

Oops! Something went wrong.

2 | 3 |
4 |

Extra template variables work here too: {$usefulErrorHelper}

5 |
6 | 7 |

Please go back and try again.

8 | 9 |

If the problem persists, please contact support.

10 | -------------------------------------------------------------------------------- /modules/servers/mailcow/templates/manage.tpl: -------------------------------------------------------------------------------- 1 |

Custom Client Area Page

2 | 3 |

This is an example of an additional custom page within a module's client area product management pages.

4 | 5 |

Everything that is available in the overview is also available in this template file along with any custom defined template variables.

6 | 7 |
8 | 9 |
10 |
11 | {$LANG.orderproduct} 12 |
13 |
14 | {$groupname} - {$product} 15 |
16 |
17 | 18 |
19 |
20 | Extra Variable 1 21 |
22 |
23 | {$extraVariable1} 24 |
25 |
26 | 27 |
28 |
29 | Extra Variable 2 30 |
31 |
32 | {$extraVariable2} 33 |
34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 | 42 | 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /modules/servers/mailcow/templates/overview.tpl: -------------------------------------------------------------------------------- 1 |

Overview

2 | 3 |

Overview output goes here...

4 | 5 |

Please Remember: When overriding the default product overview output, it is important to provide the product details and information that are normally displayed on this page. These are provided below.

6 | 7 |
8 | Any variables you define inside the ClientArea module function can also be accessed and used here, for example: {$extraVariable1} & {$extraVariable2} 9 |
10 | 11 |

{$LANG.clientareaproductdetails}

12 | 13 |
14 | 15 |
16 |
17 | {$LANG.clientareahostingregdate} 18 |
19 |
20 | {$regdate} 21 |
22 |
23 | 24 |
25 |
26 | {$LANG.orderproduct} 27 |
28 |
29 | {$groupname} - {$product} 30 |
31 |
32 | 33 | {if $type eq "server"} 34 | {if $domain} 35 |
36 |
37 | {$LANG.serverhostname} 38 |
39 |
40 | {$domain} 41 |
42 |
43 | {/if} 44 | {if $dedicatedip} 45 |
46 |
47 | {$LANG.primaryIP} 48 |
49 |
50 | {$dedicatedip} 51 |
52 |
53 | {/if} 54 | {if $assignedips} 55 |
56 |
57 | {$LANG.assignedIPs} 58 |
59 |
60 | {$assignedips|nl2br} 61 |
62 |
63 | {/if} 64 | {if $ns1 || $ns2} 65 |
66 |
67 | {$LANG.domainnameservers} 68 |
69 |
70 | {$ns1}
{$ns2} 71 |
72 |
73 | {/if} 74 | {else} 75 | {if $domain} 76 |
77 |
78 | {$LANG.orderdomain} 79 |
80 |
81 | {$domain} 82 | {$LANG.visitwebsite} 83 |
84 |
85 | {/if} 86 | {if $username} 87 |
88 |
89 | {$LANG.serverusername} 90 |
91 |
92 | {$username} 93 |
94 |
95 | {/if} 96 | {if $serverdata} 97 |
98 |
99 | {$LANG.servername} 100 |
101 |
102 | {$serverdata.hostname} 103 |
104 |
105 |
106 |
107 | {$LANG.domainregisternsip} 108 |
109 |
110 | {$serverdata.ipaddress} 111 |
112 |
113 | {if $serverdata.nameserver1 || $serverdata.nameserver2 || $serverdata.nameserver3 || $serverdata.nameserver4 || $serverdata.nameserver5} 114 |
115 |
116 | {$LANG.domainnameservers} 117 |
118 |
119 | {if $serverdata.nameserver1}{$serverdata.nameserver1} ({$serverdata.nameserver1ip})
{/if} 120 | {if $serverdata.nameserver2}{$serverdata.nameserver2} ({$serverdata.nameserver2ip})
{/if} 121 | {if $serverdata.nameserver3}{$serverdata.nameserver3} ({$serverdata.nameserver3ip})
{/if} 122 | {if $serverdata.nameserver4}{$serverdata.nameserver4} ({$serverdata.nameserver4ip})
{/if} 123 | {if $serverdata.nameserver5}{$serverdata.nameserver5} ({$serverdata.nameserver5ip})
{/if} 124 |
125 |
126 | {/if} 127 | {/if} 128 | {/if} 129 | 130 | {if $dedicatedip} 131 |
132 |
133 | {$LANG.domainregisternsip} 134 |
135 |
136 | {$dedicatedip} 137 |
138 |
139 | {/if} 140 | 141 | {foreach from=$configurableoptions item=configoption} 142 |
143 |
144 | {$configoption.optionname} 145 |
146 |
147 | {if $configoption.optiontype eq 3} 148 | {if $configoption.selectedqty} 149 | {$LANG.yes} 150 | {else} 151 | {$LANG.no} 152 | {/if} 153 | {elseif $configoption.optiontype eq 4} 154 | {$configoption.selectedqty} x {$configoption.selectedoption} 155 | {else} 156 | {$configoption.selectedoption} 157 | {/if} 158 |
159 |
160 | {/foreach} 161 | 162 | {foreach from=$productcustomfields item=customfield} 163 |
164 |
165 | {$customfield.name} 166 |
167 |
168 | {$customfield.value} 169 |
170 |
171 | {/foreach} 172 | 173 | {if $lastupdate} 174 |
175 |
176 | {$LANG.clientareadiskusage} 177 |
178 |
179 | {$diskusage}MB / {$disklimit}MB ({$diskpercent}) 180 |
181 |
182 |
183 |
184 | {$LANG.clientareabwusage} 185 |
186 |
187 | {$bwusage}MB / {$bwlimit}MB ({$bwpercent}) 188 |
189 |
190 | {/if} 191 | 192 |
193 |
194 | {$LANG.orderpaymentmethod} 195 |
196 |
197 | {$paymentmethod} 198 |
199 |
200 | 201 |
202 |
203 | {$LANG.firstpaymentamount} 204 |
205 |
206 | {$firstpaymentamount} 207 |
208 |
209 | 210 |
211 |
212 | {$LANG.recurringamount} 213 |
214 |
215 | {$recurringamount} 216 |
217 |
218 | 219 |
220 |
221 | {$LANG.clientareahostingnextduedate} 222 |
223 |
224 | {$nextduedate} 225 |
226 |
227 | 228 |
229 |
230 | {$LANG.orderbillingcycle} 231 |
232 |
233 | {$billingcycle} 234 |
235 |
236 | 237 |
238 |
239 | {$LANG.clientareastatus} 240 |
241 |
242 | {$status} 243 |
244 |
245 | 246 | {if $suspendreason} 247 |
248 |
249 | {$LANG.suspendreason} 250 |
251 |
252 | {$suspendreason} 253 |
254 |
255 | {/if} 256 | 257 |
258 | 259 |
260 |
261 |
262 | 263 | 264 | 267 |
268 |
269 | 270 | {if $packagesupgrade} 271 | 276 | {/if} 277 | 278 | 287 |
288 | --------------------------------------------------------------------------------