System.php 53.9 KB
Newer Older
totten's avatar
totten committed
1 2 3
<?php
/*
 +--------------------------------------------------------------------+
totten's avatar
totten committed
4
 | CiviCRM version 5                                                  |
totten's avatar
totten committed
5
 +--------------------------------------------------------------------+
Seamus Lee's avatar
Seamus Lee committed
6
 | Copyright CiviCRM LLC (c) 2004-2019                                |
totten's avatar
totten committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 +--------------------------------------------------------------------+
 | This file is a part of CiviCRM.                                    |
 |                                                                    |
 | CiviCRM is free software; you can copy, modify, and distribute it  |
 | under the terms of the GNU Affero General Public License           |
 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
 |                                                                    |
 | CiviCRM is distributed in the hope that it will be useful, but     |
 | WITHOUT ANY WARRANTY; without even the implied warranty of         |
 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
 | See the GNU Affero General Public License for more details.        |
 |                                                                    |
 | You should have received a copy of the GNU Affero General Public   |
 | License and the CiviCRM Licensing Exception along                  |
 | with this program; if not, contact CiviCRM LLC                     |
 | at info[AT]civicrm[DOT]org. If you have questions about the        |
 | GNU Affero General Public License or the licensing of CiviCRM,     |
 | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
 +--------------------------------------------------------------------+
26
 */
totten's avatar
totten committed
27 28 29 30

/**
 *
 * @package CRM
Seamus Lee's avatar
Seamus Lee committed
31
 * @copyright CiviCRM LLC (c) 2004-2019
totten's avatar
totten committed
32 33 34 35
 */

/**
 * System wide utilities.
36 37 38 39 40 41 42 43
 *
 * Provides a collection of Civi utilities + access to the CMS-dependant utilities
 *
 * FIXME: This is a massive and random collection that could be split into smaller services
 *
 * @method static mixed permissionDenied() Show access denied screen.
 * @method static mixed logout() Log out the current user.
 * @method static mixed updateCategories() Clear CMS caches related to the user registration/profile forms.
44
 * @method static appendBreadCrumb(array $breadCrumbs) Append an additional breadcrumb tag to the existing breadcrumbs.
45 46 47 48 49 50 51 52 53
 * @method static resetBreadCrumb() Reset an additional breadcrumb tag to the existing breadcrumb.
 * @method static addHTMLHead(string $bc) Append a string to the head of the HTML file.
 * @method static string postURL(int $action) Determine the post URL for a form.
 * @method static string|null getUFLocale() Get the locale of the CMS.
 * @method static bool setUFLocale(string $civicrm_language) Set the locale of the CMS.
 * @method static bool isUserLoggedIn() Check if user is logged in.
 * @method static int getLoggedInUfID() Get current logged in user id.
 * @method static setHttpHeader(string $name, string $value) Set http header.
 * @method static array synchronizeUsers() Create CRM contacts for all existing CMS users.
colemanw's avatar
colemanw committed
54
 * @method static appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) Callback for hook_civicrm_coreResourceList.
55
 * @method static alterAssetUrl(\Civi\Core\Event\GenericHookEvent $e) Callback for hook_civicrm_getAssetUrl.
56
 * @method static sendResponse(\Psr\Http\Message\ResponseInterface $response) function to handle RepsoseInterface for delivering HTTP Responses.
totten's avatar
totten committed
57 58 59
 */
class CRM_Utils_System {

60
  public static $_callbacks = NULL;
totten's avatar
totten committed
61

62
  /**
63 64
   * @var string
   *   Page title
65
   */
66
  public static $title = '';
67

68 69 70 71 72 73 74 75 76
  /**
   * Access methods in the appropriate CMS class
   *
   * @param $name
   * @param $arguments
   * @return mixed
   */
  public static function __callStatic($name, $arguments) {
    $userSystem = CRM_Core_Config::singleton()->userSystem;
77
    return call_user_func_array([$userSystem, $name], $arguments);
78 79
  }

totten's avatar
totten committed
80
  /**
Robert C. Sheets's avatar
Robert C. Sheets committed
81
   * Compose a new URL string from the current URL string.
82
   *
totten's avatar
totten committed
83 84 85
   * Used by all the framework components, specifically,
   * pager, sort and qfc
   *
86 87 88 89 90 91
   * @param string $urlVar
   *   The url variable being considered (i.e. crmPageID, crmSortID etc).
   * @param bool $includeReset
   *   (optional) Whether to include the reset GET string (if present).
   * @param bool $includeForce
   *   (optional) Whether to include the force GET string (if present).
92
   * @param string $path
93
   *   (optional) The path to use for the new url.
Eileen McNaughton's avatar
Eileen McNaughton committed
94
   * @param bool|string $absolute
95
   *   (optional) Whether to return an absolute URL.
totten's avatar
totten committed
96
   *
97 98
   * @return string
   *   The URL fragment.
totten's avatar
totten committed
99
   */
100
  public static function makeURL($urlVar, $includeReset = FALSE, $includeForce = TRUE, $path = NULL, $absolute = FALSE) {
totten's avatar
totten committed
101 102 103 104 105 106 107 108
    if (empty($path)) {
      $config = CRM_Core_Config::singleton();
      $path = CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET);
      if (empty($path)) {
        return '';
      }
    }

109
    return self::url(
110 111 112 113
        $path,
        CRM_Utils_System::getLinksUrl($urlVar, $includeReset, $includeForce),
        $absolute
      );
totten's avatar
totten committed
114 115 116
  }

  /**
117 118
   * Get the query string and clean it up.
   *
Robert C. Sheets's avatar
Robert C. Sheets committed
119
   * Strips some variables that should not be propagated, specifically variables
120
   * like 'reset'. Also strips any side-affect actions (e.g. export).
totten's avatar
totten committed
121 122 123
   *
   * This function is copied mostly verbatim from Pager.php (_getLinksUrl)
   *
124 125 126
   * @param string $urlVar
   *   The URL variable being considered (e.g. crmPageID, crmSortID etc).
   * @param bool $includeReset
Robert C. Sheets's avatar
Robert C. Sheets committed
127 128
   *   (optional) By default this is FALSE, meaning that the reset parameter
   *   is skipped. Set to TRUE to leave the reset parameter as-is.
129
   * @param bool $includeForce
Robert C. Sheets's avatar
Robert C. Sheets committed
130
   *   (optional)
131
   * @param bool $skipUFVar
Robert C. Sheets's avatar
Robert C. Sheets committed
132
   *   (optional)
totten's avatar
totten committed
133 134 135
   *
   * @return string
   */
136
  public static function getLinksUrl($urlVar, $includeReset = FALSE, $includeForce = TRUE, $skipUFVar = TRUE) {
totten's avatar
totten committed
137
    // Sort out query string to prevent messy urls
138 139 140
    $querystring = [];
    $qs = [];
    $arrays = [];
totten's avatar
totten committed
141 142 143 144 145 146 147 148 149

    if (!empty($_SERVER['QUERY_STRING'])) {
      $qs = explode('&', str_replace('&amp;', '&', $_SERVER['QUERY_STRING']));
      for ($i = 0, $cnt = count($qs); $i < $cnt; $i++) {
        // check first if exist a pair
        if (strstr($qs[$i], '=') !== FALSE) {
          list($name, $value) = explode('=', $qs[$i]);
          if ($name != $urlVar) {
            $name = rawurldecode($name);
150
            // check for arrays in parameters: site.php?foo[]=1&foo[]=2&foo[]=3
totten's avatar
totten committed
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
            if ((strpos($name, '[') !== FALSE) &&
              (strpos($name, ']') !== FALSE)
            ) {
              $arrays[] = $qs[$i];
            }
            else {
              $qs[$name] = $value;
            }
          }
        }
        else {
          $qs[$qs[$i]] = '';
        }
        unset($qs[$i]);
      }
    }

    if ($includeForce) {
      $qs['force'] = 1;
    }

colemanw's avatar
colemanw committed
172
    // Ok this is a big assumption but usually works
Robert C. Sheets's avatar
Robert C. Sheets committed
173 174
    // If we are in snippet mode, retain the 'section' param, if not, get rid
    // of it.
colemanw's avatar
colemanw committed
175 176 177 178 179 180
    if (!empty($qs['snippet'])) {
      unset($qs['snippet']);
    }
    else {
      unset($qs['section']);
    }
totten's avatar
totten committed
181 182 183 184 185 186 187 188 189 190 191 192 193 194

    if ($skipUFVar) {
      $config = CRM_Core_Config::singleton();
      unset($qs[$config->userFrameworkURLVar]);
    }

    foreach ($qs as $name => $value) {
      if ($name != 'reset' || $includeReset) {
        $querystring[] = $name . '=' . $value;
      }
    }

    $querystring = array_merge($querystring, array_unique($arrays));

195
    $url = implode('&', $querystring);
196
    if ($urlVar) {
197
      $url .= (!empty($querystring) ? '&' : '') . $urlVar . '=';
198 199 200
    }

    return $url;
totten's avatar
totten committed
201 202 203
  }

  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
204
   * If we are using a theming system, invoke theme, else just print the content.
totten's avatar
totten committed
205
   *
206 207
   * @param string $content
   *   The content that will be themed.
208
   * @param bool $print
209 210 211
   *   (optional) Are we displaying to the screen or bypassing theming?
   * @param bool $maintenance
   *   (optional) For maintenance mode.
totten's avatar
totten committed
212
   *
213
   * @return string
totten's avatar
totten committed
214
   */
colemanw's avatar
colemanw committed
215
  public static function theme(
totten's avatar
totten committed
216
    &$content,
totten's avatar
totten committed
217
    $print = FALSE,
totten's avatar
totten committed
218 219
    $maintenance = FALSE
  ) {
220
    return CRM_Core_Config::singleton()->userSystem->theme($content, $print, $maintenance);
totten's avatar
totten committed
221 222 223
  }

  /**
224
   * Generate a query string if input is an array.
totten's avatar
totten committed
225
   *
226
   * @param array|string $query
eileenmcnaugton's avatar
eileenmcnaugton committed
227
   *
228
   * @return string
totten's avatar
totten committed
229
   */
230
  public static function makeQueryString($query) {
totten's avatar
totten committed
231 232 233 234 235 236 237 238 239 240 241
    if (is_array($query)) {
      $buf = '';
      foreach ($query as $key => $value) {
        $buf .= ($buf ? '&' : '') . urlencode($key) . '=' . urlencode($value);
      }
      $query = $buf;
    }
    return $query;
  }

  /**
242
   * Generate an internal CiviCRM URL.
totten's avatar
totten committed
243
   *
244 245 246 247 248 249
   * @param string $path
   *   The path being linked to, such as "civicrm/add".
   * @param array|string $query
   *   A query string to append to the link, or an array of key-value pairs.
   * @param bool $absolute
   *   Whether to force the output to be an absolute link (beginning with a
Robert C. Sheets's avatar
Robert C. Sheets committed
250
   *   URI-scheme such as 'http:'). Useful for links that will be displayed
251 252 253
   *   outside the site, such as in an RSS feed.
   * @param string $fragment
   *   A fragment identifier (named anchor) to append to the link.
254
   * @param bool $htmlize
colemanw's avatar
colemanw committed
255
   *   Whether to encode special html characters such as &.
256
   * @param bool $frontend
colemanw's avatar
colemanw committed
257
   *   This link should be to the CMS front end (applies to WP & Joomla).
258
   * @param bool $forceBackend
colemanw's avatar
colemanw committed
259
   *   This link should be to the CMS back end (applies to WP & Joomla).
eileenmcnaugton's avatar
eileenmcnaugton committed
260
   *
261 262
   * @return string
   *   An HTML string containing a link to the given path.
totten's avatar
totten committed
263
   */
colemanw's avatar
colemanw committed
264
  public static function url(
totten's avatar
totten committed
265
    $path = NULL,
totten's avatar
totten committed
266
    $query = NULL,
totten's avatar
totten committed
267 268
    $absolute = FALSE,
    $fragment = NULL,
totten's avatar
totten committed
269
    $htmlize = TRUE,
totten's avatar
totten committed
270 271 272 273 274
    $frontend = FALSE,
    $forceBackend = FALSE
  ) {
    $query = self::makeQueryString($query);

colemanw's avatar
colemanw committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
    // Legacy handling for when the system passes around html escaped strings
    if (strstr($query, '&amp;')) {
      $query = html_entity_decode($query);
    }

    // Extract fragment from path or query if munged together
    if ($query && strstr($query, '#')) {
      list($path, $fragment) = explode('#', $query);
    }
    if ($path && strstr($path, '#')) {
      list($path, $fragment) = explode('#', $path);
    }

    // Extract query from path if munged together
    if ($path && strstr($path, '?')) {
      list($path, $extraQuery) = explode('?', $path);
      $query = $extraQuery . ($query ? "&$query" : '');
totten's avatar
totten committed
292 293 294
    }

    $config = CRM_Core_Config::singleton();
colemanw's avatar
colemanw committed
295 296 297 298 299 300 301
    $url = $config->userSystem->url($path, $query, $absolute, $fragment, $frontend, $forceBackend);

    if ($htmlize) {
      $url = htmlentities($url);
    }

    return $url;
totten's avatar
totten committed
302 303
  }

304 305 306 307 308 309 310 311 312 313 314 315
  /**
   * Path of the current page e.g. 'civicrm/contact/view'
   *
   * @return string|null
   */
  public static function getUrlPath() {
    if (isset($_GET[CRM_Core_Config::singleton()->userFrameworkURLVar])) {
      return $_GET[CRM_Core_Config::singleton()->userFrameworkURLVar];
    }
    return NULL;
  }

Eileen McNaughton's avatar
Eileen McNaughton committed
316
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
317 318 319 320 321
   * Get href.
   *
   * @param string $text
   * @param string $path
   * @param string|array $query
Eileen McNaughton's avatar
Eileen McNaughton committed
322
   * @param bool $absolute
eileenmcnaugton's avatar
eileenmcnaugton committed
323
   * @param string $fragment
Eileen McNaughton's avatar
Eileen McNaughton committed
324 325 326 327 328 329
   * @param bool $htmlize
   * @param bool $frontend
   * @param bool $forceBackend
   *
   * @return string
   */
colemanw's avatar
colemanw committed
330
  public static function href(
totten's avatar
totten committed
331
    $text, $path = NULL, $query = NULL, $absolute = TRUE,
totten's avatar
totten committed
332 333 334 335 336 337 338
    $fragment = NULL, $htmlize = TRUE, $frontend = FALSE, $forceBackend = FALSE
  ) {
    $url = self::url($path, $query, $absolute, $fragment, $htmlize, $frontend, $forceBackend);
    return "<a href=\"$url\">$text</a>";
  }

  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
339
   * What menu path are we currently on. Called for the primary tpl.
totten's avatar
totten committed
340
   *
341 342
   * @return string
   *   the current menu path
totten's avatar
totten committed
343
   */
344
  public static function currentPath() {
totten's avatar
totten committed
345 346 347 348 349
    $config = CRM_Core_Config::singleton();
    return trim(CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET), '/');
  }

  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
350
   * Called from a template to compose a url.
totten's avatar
totten committed
351
   *
352 353
   * @param array $params
   *   List of parameters.
totten's avatar
totten committed
354
   *
355 356
   * @return string
   *   url
totten's avatar
totten committed
357
   */
358
  public static function crmURL($params) {
totten's avatar
totten committed
359 360 361 362 363
    $p = CRM_Utils_Array::value('p', $params);
    if (!isset($p)) {
      $p = self::currentPath();
    }

lobo's avatar
lobo committed
364 365
    return self::url(
      $p,
totten's avatar
totten committed
366 367 368 369 370 371 372 373 374 375
      CRM_Utils_Array::value('q', $params),
      CRM_Utils_Array::value('a', $params, FALSE),
      CRM_Utils_Array::value('f', $params),
      CRM_Utils_Array::value('h', $params, TRUE),
      CRM_Utils_Array::value('fe', $params, FALSE),
      CRM_Utils_Array::value('fb', $params, FALSE)
    );
  }

  /**
376
   * Sets the title of the page.
totten's avatar
totten committed
377 378
   *
   * @param string $title
379
   *   Document title - plain text only
totten's avatar
totten committed
380
   * @param string $pageTitle
381
   *   Page title (if different) - may include html
totten's avatar
totten committed
382
   */
383
  public static function setTitle($title, $pageTitle = NULL) {
384
    self::$title = $title;
totten's avatar
totten committed
385 386 387 388 389
    $config = CRM_Core_Config::singleton();
    return $config->userSystem->setTitle($title, $pageTitle);
  }

  /**
390
   * Figures and sets the userContext.
totten's avatar
totten committed
391
   *
eileenmcnaugton's avatar
eileenmcnaugton committed
392
   * Uses the referrer if valid else uses the default.
393 394
   *
   * @param array $names
395
   *   Referrer should match any str in this array.
396 397
   * @param string $default
   *   (optional) The default userContext if no match found.
totten's avatar
totten committed
398
   */
399
  public static function setUserContext($names, $default = NULL) {
totten's avatar
totten committed
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    $url = $default;

    $session = CRM_Core_Session::singleton();
    $referer = CRM_Utils_Array::value('HTTP_REFERER', $_SERVER);

    if ($referer && !empty($names)) {
      foreach ($names as $name) {
        if (strstr($referer, $name)) {
          $url = $referer;
          break;
        }
      }
    }

    if ($url) {
      $session->pushUserContext($url);
    }
  }

  /**
420
   * Gets a class name for an object.
totten's avatar
totten committed
421
   *
422 423
   * @param object $object
   *   Object whose class name is needed.
totten's avatar
totten committed
424
   *
425 426
   * @return string
   *   The class name of the object.
totten's avatar
totten committed
427
   */
428
  public static function getClassName($object) {
totten's avatar
totten committed
429 430 431 432
    return get_class($object);
  }

  /**
433
   * Redirect to another URL.
totten's avatar
totten committed
434
   *
435 436
   * @param string $url
   *   The URL to provide to the browser via the Location header.
437 438
   * @param array $context
   *   Optional additional information for the hook.
totten's avatar
totten committed
439
   */
440
  public static function redirect($url = NULL, $context = []) {
totten's avatar
totten committed
441 442 443 444 445 446
    if (!$url) {
      $url = self::url('civicrm/dashboard', 'reset=1');
    }
    // replace the &amp; characters with &
    // this is kinda hackish but not sure how to do it right
    $url = str_replace('&amp;', '&', $url);
colemanw's avatar
colemanw committed
447

448 449 450 451 452 453
    $context['output'] = CRM_Utils_Array::value('snippet', $_GET);

    $parsedUrl = CRM_Utils_Url::parseUrl($url);
    CRM_Utils_Hook::alterRedirect($parsedUrl, $context);
    $url = CRM_Utils_Url::unparseUrl($parsedUrl);

colemanw's avatar
colemanw committed
454
    // If we are in a json context, respond appropriately
455
    if ($context['output'] === 'json') {
456
      CRM_Core_Page_AJAX::returnJsonResponse([
colemanw's avatar
colemanw committed
457 458
        'status' => 'redirect',
        'userContext' => $url,
459
      ]);
colemanw's avatar
colemanw committed
460 461
    }

462
    self::setHttpHeader('Location', $url);
totten's avatar
totten committed
463 464 465 466
    self::civiExit();
  }

  /**
467 468 469
   * Redirect to another URL using JavaScript.
   *
   * Use an html based file with javascript embedded to redirect to another url
totten's avatar
totten committed
470 471
   * This prevent the too many redirect errors emitted by various browsers
   *
472 473 474 475 476 477
   * @param string $url
   *   (optional) The destination URL.
   * @param string $title
   *   (optional) The page title to use for the redirect page.
   * @param string $message
   *   (optional) The message to provide in the body of the redirect page.
totten's avatar
totten committed
478
   */
colemanw's avatar
colemanw committed
479
  public static function jsRedirect(
totten's avatar
totten committed
480 481
    $url = NULL,
    $title = NULL,
totten's avatar
totten committed
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
    $message = NULL
  ) {
    if (!$url) {
      $url = self::url('civicrm/dashboard', 'reset=1');
    }

    if (!$title) {
      $title = ts('CiviCRM task in progress');
    }

    if (!$message) {
      $message = ts('A long running CiviCRM task is currently in progress. This message will be refreshed till the task is completed');
    }

    // replace the &amp; characters with &
    // this is kinda hackish but not sure how to do it right
    $url = str_replace('&amp;', '&', $url);

    $template = CRM_Core_Smarty::singleton();
    $template->assign('redirectURL', $url);
    $template->assign('title', $title);
    $template->assign('message', $message);

    $html = $template->fetch('CRM/common/redirectJS.tpl');

    echo $html;

    self::civiExit();
  }

  /**
513
   * Get the base URL of the system.
totten's avatar
totten committed
514 515 516
   *
   * @return string
   */
517
  public static function baseURL() {
totten's avatar
totten committed
518 519 520 521
    $config = CRM_Core_Config::singleton();
    return $config->userFrameworkBaseURL;
  }

522
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
523 524 525 526 527 528
   * Authenticate or abort.
   *
   * @param string $message
   * @param bool $abort
   *
   * @return bool
529
   */
530
  public static function authenticateAbort($message, $abort) {
totten's avatar
totten committed
531 532 533 534 535 536 537 538 539
    if ($abort) {
      echo $message;
      self::civiExit(0);
    }
    else {
      return FALSE;
    }
  }

540
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
541 542
   * Authenticate key.
   *
543 544
   * @param bool $abort
   *   (optional) Whether to exit; defaults to true.
545 546
   *
   * @return bool
547
   */
548
  public static function authenticateKey($abort = TRUE) {
totten's avatar
totten committed
549 550 551 552 553 554
    // also make sure the key is sent and is valid
    $key = trim(CRM_Utils_Array::value('key', $_REQUEST));

    $docAdd = "More info at:" . CRM_Utils_System::docURL2("Managing Scheduled Jobs", TRUE, NULL, NULL, NULL, "wiki");

    if (!$key) {
lobo's avatar
lobo committed
555 556
      return self::authenticateAbort(
        "ERROR: You need to send a valid key to execute this file. " . $docAdd . "\n",
totten's avatar
totten committed
557 558 559 560 561 562
        $abort
      );
    }

    $siteKey = defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY : NULL;

lobo's avatar
lobo committed
563 564 565
    if (!$siteKey || empty($siteKey)) {
      return self::authenticateAbort(
        "ERROR: You need to set a valid site key in civicrm.settings.php. " . $docAdd . "\n",
totten's avatar
totten committed
566 567 568 569 570
        $abort
      );
    }

    if (strlen($siteKey) < 8) {
lobo's avatar
lobo committed
571 572
      return self::authenticateAbort(
        "ERROR: Site key needs to be greater than 7 characters in civicrm.settings.php. " . $docAdd . "\n",
totten's avatar
totten committed
573 574 575 576 577
        $abort
      );
    }

    if ($key !== $siteKey) {
lobo's avatar
lobo committed
578 579
      return self::authenticateAbort(
        "ERROR: Invalid key value sent. " . $docAdd . "\n",
totten's avatar
totten committed
580 581 582 583 584 585 586
        $abort
      );
    }

    return TRUE;
  }

587
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
588 589
   * Authenticate script.
   *
Eileen McNaughton's avatar
Eileen McNaughton committed
590
   * @param bool $abort
eileenmcnaugton's avatar
eileenmcnaugton committed
591 592
   * @param string $name
   * @param string $pass
Eileen McNaughton's avatar
Eileen McNaughton committed
593 594 595 596
   * @param bool $storeInSession
   * @param bool $loadCMSBootstrap
   * @param bool $requireKey
   *
597 598
   * @return bool
   */
599
  public static function authenticateScript($abort = TRUE, $name = NULL, $pass = NULL, $storeInSession = TRUE, $loadCMSBootstrap = TRUE, $requireKey = TRUE) {
600
    // auth to make sure the user has a login/password to do a shell operation
totten's avatar
totten committed
601 602 603 604 605 606 607 608
    // later on we'll link this to acl's
    if (!$name) {
      $name = trim(CRM_Utils_Array::value('name', $_REQUEST));
      $pass = trim(CRM_Utils_Array::value('pass', $_REQUEST));
    }

    // its ok to have an empty password
    if (!$name) {
lobo's avatar
lobo committed
609 610
      return self::authenticateAbort(
        "ERROR: You need to send a valid user name and password to execute this file\n",
totten's avatar
totten committed
611 612 613 614
        $abort
      );
    }

615
    if ($requireKey && !self::authenticateKey($abort)) {
totten's avatar
totten committed
616 617 618 619 620
      return FALSE;
    }

    $result = CRM_Utils_System::authenticate($name, $pass, $loadCMSBootstrap);
    if (!$result) {
lobo's avatar
lobo committed
621 622
      return self::authenticateAbort(
        "ERROR: Invalid username and/or password\n",
totten's avatar
totten committed
623 624 625 626 627 628 629
        $abort
      );
    }
    elseif ($storeInSession) {
      // lets store contact id and user id in session
      list($userID, $ufID, $randomNumber) = $result;
      if ($userID && $ufID) {
630
        $config = CRM_Core_Config::singleton();
631
        $config->userSystem->setUserSession([$userID, $ufID]);
totten's avatar
totten committed
632 633
      }
      else {
lobo's avatar
lobo committed
634 635
        return self::authenticateAbort(
          "ERROR: Unexpected error, could not match userID and contactID",
totten's avatar
totten committed
636 637 638 639 640 641 642 643 644
          $abort
        );
      }
    }

    return $result;
  }

  /**
645
   * Authenticate the user against the uf db.
totten's avatar
totten committed
646
   *
Dmitry Smirnov's avatar
Dmitry Smirnov committed
647
   * In case of successful authentication, returns an array consisting of
648 649
   * (contactID, ufID, unique string). Returns FALSE if authentication is
   * unsuccessful.
totten's avatar
totten committed
650
   *
651 652 653 654 655
   * @param string $name
   *   The username.
   * @param string $password
   *   The password.
   * @param bool $loadCMSBootstrap
eileenmcnaugton's avatar
eileenmcnaugton committed
656
   * @param string $realPath
657 658
   *
   * @return false|array
totten's avatar
totten committed
659
   */
660
  public static function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
totten's avatar
totten committed
661
    $config = CRM_Core_Config::singleton();
lobo's avatar
lobo committed
662

663 664 665 666
    /* Before we do any loading, let's start the session and write to it.
     * We typically call authenticate only when we need to bootstrap the CMS
     * directly via Civi and hence bypass the normal CMS auth and bootstrap
     * process typically done in CLI and cron scripts. See: CRM-12648
667 668 669 670
     *
     * Q: Can we move this to the userSystem class so that it can be tuned
     * per-CMS? For example, when dealing with UnitTests UF, there's no
     * userFrameworkDSN.
671
     */
lobo's avatar
lobo committed
672
    $session = CRM_Core_Session::singleton();
673
    $session->set('civicrmInitSession', TRUE);
lobo's avatar
lobo committed
674

675 676 677
    if ($config->userFrameworkDSN) {
      $dbDrupal = DB::connect($config->userFrameworkDSN);
    }
totten's avatar
totten committed
678 679 680 681
    return $config->userSystem->authenticate($name, $password, $loadCMSBootstrap, $realPath);
  }

  /**
682
   * Set a message in the UF to display to a user.
totten's avatar
totten committed
683
   *
684 685
   * @param string $message
   *   The message to set.
totten's avatar
totten committed
686
   */
687
  public static function setUFMessage($message) {
totten's avatar
totten committed
688 689 690 691
    $config = CRM_Core_Config::singleton();
    return $config->userSystem->setMessage($message);
  }

692 693 694
  /**
   * Determine whether a value is null-ish.
   *
eileenmcnaugton's avatar
eileenmcnaugton committed
695
   * @param mixed $value
696
   *   The value to check for null.
eileenmcnaugton's avatar
eileenmcnaugton committed
697
   *
698 699
   * @return bool
   */
700
  public static function isNull($value) {
totten's avatar
totten committed
701 702 703 704 705
    // FIXME: remove $value = 'null' string test when we upgrade our DAO code to handle passing null in a better way.
    if (!isset($value) || $value === NULL || $value === '' || $value === 'null') {
      return TRUE;
    }
    if (is_array($value)) {
Andreas Hennings's avatar
Andreas Hennings committed
706
      // @todo Reuse of the $value variable = asking for trouble.
totten's avatar
totten committed
707
      foreach ($value as $key => $value) {
708
        if (in_array($key, CRM_Core_DAO::acceptedSQLOperators(), TRUE) || !self::isNull($value)) {
totten's avatar
totten committed
709 710 711 712 713 714 715 716
          return FALSE;
        }
      }
      return TRUE;
    }
    return FALSE;
  }

717 718 719 720 721 722 723
  /**
   * Obscure all but the last few digits of a credit card number.
   *
   * @param string $number
   *   The credit card number to obscure.
   * @param int $keep
   *   (optional) The number of digits to preserve unmodified.
eileenmcnaugton's avatar
eileenmcnaugton committed
724
   *
725 726 727
   * @return string
   *   The obscured credit card number.
   */
728
  public static function mungeCreditCard($number, $keep = 4) {
totten's avatar
totten committed
729 730 731 732 733 734 735 736
    $number = trim($number);
    if (empty($number)) {
      return NULL;
    }
    $replace = str_repeat('*', strlen($number) - $keep);
    return substr_replace($number, $replace, 0, -$keep);
  }

737
  /**
738 739 740 741
   * Determine which PHP modules are loaded.
   *
   * @return array
   */
JoeMurray's avatar
JoeMurray committed
742
  private static function parsePHPModules() {
totten's avatar
totten committed
743 744 745 746 747
    ob_start();
    phpinfo(INFO_MODULES);
    $s = ob_get_contents();
    ob_end_clean();

totten's avatar
totten committed
748 749 750 751
    $s = strip_tags($s, '<h2><th><td>');
    $s = preg_replace('/<th[^>]*>([^<]+)<\/th>/', "<info>\\1</info>", $s);
    $s = preg_replace('/<td[^>]*>([^<]+)<\/td>/', "<info>\\1</info>", $s);
    $vTmp = preg_split('/(<h2>[^<]+<\/h2>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE);
752
    $vModules = [];
totten's avatar
totten committed
753 754 755 756
    for ($i = 1; $i < count($vTmp); $i++) {
      if (preg_match('/<h2>([^<]+)<\/h2>/', $vTmp[$i], $vMat)) {
        $vName = trim($vMat[1]);
        $vTmp2 = explode("\n", $vTmp[$i + 1]);
totten's avatar
totten committed
757
        foreach ($vTmp2 as $vOne) {
totten's avatar
totten committed
758
          $vPat = '<info>([^<]+)<\/info>';
totten's avatar
totten committed
759 760 761 762
          $vPat3 = "/$vPat\s*$vPat\s*$vPat/";
          $vPat2 = "/$vPat\s*$vPat/";
          // 3cols
          if (preg_match($vPat3, $vOne, $vMat)) {
763
            $vModules[$vName][trim($vMat[1])] = [trim($vMat[2]), trim($vMat[3])];
totten's avatar
totten committed
764 765 766 767 768 769 770 771 772 773 774
            // 2cols
          }
          elseif (preg_match($vPat2, $vOne, $vMat)) {
            $vModules[$vName][trim($vMat[1])] = trim($vMat[2]);
          }
        }
      }
    }
    return $vModules;
  }

775 776
  /**
   * Get a setting from a loaded PHP module.
eileenmcnaugton's avatar
eileenmcnaugton committed
777 778 779 780 781
   *
   * @param string $pModuleName
   * @param string $pSetting
   *
   * @return mixed
782
   */
kurund's avatar
kurund committed
783
  public static function getModuleSetting($pModuleName, $pSetting) {
totten's avatar
totten committed
784 785 786 787
    $vModules = self::parsePHPModules();
    return $vModules[$pModuleName][$pSetting];
  }

788
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
789 790 791
   * Do something no-one bothered to document.
   *
   * @param string $title
792
   *   (optional)
Eileen McNaughton's avatar
Eileen McNaughton committed
793 794
   *
   * @return mixed|string
795
   */
796
  public static function memory($title = NULL) {
totten's avatar
totten committed
797 798 799 800 801 802 803 804 805 806 807 808 809
    static $pid = NULL;
    if (!$pid) {
      $pid = posix_getpid();
    }

    $memory = str_replace("\n", '', shell_exec("ps -p" . $pid . " -o rss="));
    $memory .= ", " . time();
    if ($title) {
      CRM_Core_Error::debug_var($title, $memory);
    }
    return $memory;
  }

810
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
811 812
   * Download something or other.
   *
813 814
   * @param string $name
   * @param string $mimeType
eileenmcnaugton's avatar
eileenmcnaugton committed
815
   * @param string $buffer
816 817
   * @param string $ext
   * @param bool $output
Eileen McNaughton's avatar
Eileen McNaughton committed
818
   * @param string $disposition
819
   */
colemanw's avatar
colemanw committed
820
  public static function download(
totten's avatar
totten committed
821
    $name, $mimeType, &$buffer,
totten's avatar
totten committed
822
    $ext = NULL,
823 824
    $output = TRUE,
    $disposition = 'attachment'
totten's avatar
totten committed
825 826 827
  ) {
    $now = gmdate('D, d M Y H:i:s') . ' GMT';

828 829
    self::setHttpHeader('Content-Type', $mimeType);
    self::setHttpHeader('Expires', $now);
totten's avatar
totten committed
830

831
    // lem9 & loic1: IE needs specific headers
832
    $isIE = empty($_SERVER['HTTP_USER_AGENT']) ? FALSE : strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE');
totten's avatar
totten committed
833 834 835 836 837 838 839
    if ($ext) {
      $fileString = "filename=\"{$name}.{$ext}\"";
    }
    else {
      $fileString = "filename=\"{$name}\"";
    }
    if ($isIE) {
840 841 842
      self::setHttpHeader("Content-Disposition", "inline; $fileString");
      self::setHttpHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
      self::setHttpHeader('Pragma', 'public');
totten's avatar
totten committed
843 844
    }
    else {
845 846
      self::setHttpHeader("Content-Disposition", "$disposition; $fileString");
      self::setHttpHeader('Pragma', 'no-cache');
totten's avatar
totten committed
847 848 849 850 851 852 853 854
    }

    if ($output) {
      print $buffer;
      self::civiExit();
    }
  }

855
  /**
Robert C. Sheets's avatar
Robert C. Sheets committed
856 857 858 859 860
   * Gather and print (and possibly log) amount of used memory.
   *
   * @param string $title
   * @param bool $log
   *   (optional) Whether to log the memory usage information.
861
   */
862
  public static function xMemory($title = NULL, $log = FALSE) {
totten's avatar
totten committed
863
    $mem = (float ) xdebug_memory_usage() / (float ) (1024);
totten's avatar
totten committed
864 865 866 867 868 869 870 871 872 873 874 875
    $mem = number_format($mem, 5) . ", " . time();
    if ($log) {
      echo "<p>$title: $mem<p>";
      flush();
      CRM_Core_Error::debug_var($title, $mem);
    }
    else {
      echo "<p>$title: $mem<p>";
      flush();
    }
  }

876
  /**
Robert C. Sheets's avatar
Robert C. Sheets committed
877 878 879 880 881
   * Take a URL (or partial URL) and make it better.
   *
   * Currently, URLs pass straight through unchanged unless they are "seriously
   * malformed" (see http://us2.php.net/parse_url).
   *
882 883
   * @param string $url
   *   The URL to operate on.
eileenmcnaugton's avatar
eileenmcnaugton committed
884
   *
Robert C. Sheets's avatar
Robert C. Sheets committed
885 886
   * @return string
   *   The fixed URL.
887
   */
888
  public static function fixURL($url) {
totten's avatar
totten committed
889 890 891 892 893 894 895
    $components = parse_url($url);

    if (!$components) {
      return NULL;
    }

    // at some point we'll add code here to make sure the url is not
896
    // something that will mess up, so we need to clean it up here
totten's avatar
totten committed
897 898 899 900
    return $url;
  }

  /**
901
   * Make sure a callback is valid in the current context.
totten's avatar
totten committed
902
   *
903 904
   * @param string $callback
   *   Name of the function to check.
totten's avatar
totten committed
905
   *
906
   * @return bool
totten's avatar
totten committed
907
   */
908
  public static function validCallback($callback) {
totten's avatar
totten committed
909
    if (self::$_callbacks === NULL) {
910
      self::$_callbacks = [];
totten's avatar
totten committed
911 912 913 914 915 916 917
    }

    if (!array_key_exists($callback, self::$_callbacks)) {
      if (strpos($callback, '::') !== FALSE) {
        list($className, $methodName) = explode('::', $callback);
        $fileName = str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
        // ignore errors if any
totten's avatar
totten committed
918
        @include_once $fileName;
totten's avatar
totten committed
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
        if (!class_exists($className)) {
          self::$_callbacks[$callback] = FALSE;
        }
        else {
          // instantiate the class
          $object = new $className();
          if (!method_exists($object, $methodName)) {
            self::$_callbacks[$callback] = FALSE;
          }
          else {
            self::$_callbacks[$callback] = TRUE;
          }
        }
      }
      else {
        self::$_callbacks[$callback] = function_exists($callback);
      }
    }
    return self::$_callbacks[$callback];
  }

  /**
941 942 943 944 945 946 947 948 949
   * Like PHP's built-in explode(), but always return an array of $limit items.
   *
   * This serves as a wrapper to the PHP explode() function. In the event that
   * PHP's explode() returns an array with fewer than $limit elements, pad
   * the end of the array with NULLs.
   *
   * @param string $separator
   * @param string $string
   * @param int $limit
eileenmcnaugton's avatar
eileenmcnaugton committed
950
   *
951
   * @return string[]
totten's avatar
totten committed
952
   */
953
  public static function explode($separator, $string, $limit) {
totten's avatar
totten committed
954 955 956 957 958 959 960
    $result = explode($separator, $string, $limit);
    for ($i = count($result); $i < $limit; $i++) {
      $result[$i] = NULL;
    }
    return $result;
  }

961
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
962 963
   * Check url.
   *
964 965 966 967
   * @param string $url
   *   The URL to check.
   * @param bool $addCookie
   *   (optional)
Eileen McNaughton's avatar
Eileen McNaughton committed
968 969
   *
   * @return mixed
970
   */
971
  public static function checkURL($url, $addCookie = FALSE) {
totten's avatar
totten committed
972 973 974 975 976 977 978 979 980
    // make a GET request to $url
    $ch = curl_init($url);
    if ($addCookie) {
      curl_setopt($ch, CURLOPT_COOKIE, http_build_query($_COOKIE));
    }
    // it's quite alright to use a self-signed cert
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

    // lets capture the return stuff rather than echo
981
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
totten's avatar
totten committed
982

cividesk's avatar
cividesk committed
983 984
    // CRM-13227, CRM-14744: only return the SSL error status
    return (curl_exec($ch) !== FALSE);
totten's avatar
totten committed
985 986
  }

987 988 989 990 991 992
  /**
   * Assert that we are running on a particular PHP version.
   *
   * @param int $ver
   *   The major version of PHP that is required.
   * @param bool $abort
993
   *   (optional) Whether to fatally abort if the version requirement is not
994
   *   met. Defaults to TRUE.
eileenmcnaugton's avatar
eileenmcnaugton committed
995
   *
996 997
   * @return bool
   *   Returns TRUE if the requirement is met, FALSE if the requirement is not
Robert C. Sheets's avatar
Robert C. Sheets committed
998 999
   *   met and we're not aborting due to the failed requirement. If $abort is
   *   TRUE and the requirement fails, this function does not return.
1000
   */
1001
  public static function checkPHPVersion($ver = 5, $abort = TRUE) {
totten's avatar
totten committed
1002 1003 1004 1005 1006 1007 1008
    $phpVersion = substr(PHP_VERSION, 0, 1);
    if ($phpVersion >= $ver) {
      return TRUE;
    }

    if ($abort) {
      CRM_Core_Error::fatal(ts('This feature requires PHP Version %1 or greater',
1009
        [1 => $ver]
totten's avatar
totten committed
1010
      ));
totten's avatar
totten committed
1011 1012 1013 1014
    }
    return FALSE;
  }

1015
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
1016 1017 1018
   * Format wiki url.
   *
   * @param string $string
Eileen McNaughton's avatar
Eileen McNaughton committed
1019 1020
   * @param bool $encode
   *
1021 1022
   * @return string
   */
1023
  public static function formatWikiURL($string, $encode = FALSE) {
totten's avatar
totten committed
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
    $items = explode(' ', trim($string), 2);
    if (count($items) == 2) {
      $title = $items[1];
    }
    else {
      $title = $items[0];
    }

    // fix for CRM-4044
    $url = $encode ? self::urlEncode($items[0]) : $items[0];
    return "<a href=\"$url\">$title</a>";
  }

1037
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
1038 1039
   * Encode url.
   *
1040
   * @param string $url
Eileen McNaughton's avatar
Eileen McNaughton committed
1041 1042
   *
   * @return null|string
1043
   */
1044
  public static function urlEncode($url) {
totten's avatar
totten committed
1045 1046 1047 1048 1049
    $items = parse_url($url);
    if ($items === FALSE) {
      return NULL;
    }

1050
    if (empty($items['query'])) {
totten's avatar
totten committed
1051 1052 1053 1054 1055 1056
      return $url;
    }

    $items['query'] = urlencode($items['query']);

    $url = $items['scheme'] . '://';
1057
    if (!empty($items['user'])) {
totten's avatar
totten committed
1058 1059 1060 1061
      $url .= "{$items['user']}:{$items['pass']}@";
    }

    $url .= $items['host'];
1062
    if (!empty($items['port'])) {
totten's avatar
totten committed
1063 1064 1065 1066
      $url .= ":{$items['port']}";
    }

    $url .= "{$items['path']}?{$items['query']}";
1067
    if (!empty($items['fragment'])) {
totten's avatar
totten committed
1068 1069 1070 1071 1072 1073 1074
      $url .= "#{$items['fragment']}";
    }

    return $url;
  }

  /**
1075
   * Return the running civicrm version.
totten's avatar
totten committed
1076
   *
1077 1078
   * @return string
   *   civicrm version
totten's avatar
totten committed
1079
   */
1080
  public static function version() {
totten's avatar
totten committed
1081 1082 1083 1084
    static $version;

    if (!$version) {
      $verFile = implode(DIRECTORY_SEPARATOR,
1085
        [dirname(__FILE__), '..', '..', 'xml', 'version.xml']
totten's avatar
totten committed
1086 1087
      );
      if (file_exists($verFile)) {
1088 1089 1090
        $str = file_get_contents($verFile);
        $xmlObj = simplexml_load_string($str);
        $version = (string) $xmlObj->version_no;
totten's avatar
totten committed
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
      }

      // pattern check
      if (!CRM_Utils_System::isVersionFormatValid($version)) {
        CRM_Core_Error::fatal('Unknown codebase version.');
      }
    }

    return $version;
  }

1102
  /**
eileenmcnaugton's avatar
eileenmcnaugton committed
1103
   * Gives the first two parts of the version string E.g. 6.1.
1104 1105 1106 1107 1108 1109 1110 1111
   *
   * @return string
   */
  public static function majorVersion() {
    list($a, $b) = explode('.', self::version());
    return "$a.$b";
  }

1112 1113 1114 1115 1116
  /**
   * Determines whether a string is a valid CiviCRM version string.
   *
   * @param string $version
   *   Version string to be checked.
eileenmcnaugton's avatar
eileenmcnaugton committed
1117
   *
1118 1119
   * @return bool
   */
1120
  public static function isVersionFormatValid($version) {
totten's avatar
totten committed
1121 1122 1123
    return preg_match("/^(\d{1,2}\.){2,3}(\d{1,2}|(alpha|beta)\d{1,2})(\.upgrade)?$/", $version);
  }

1124 1125 1126
  /**
   * Wraps or emulates PHP's getallheaders() function.
   */
1127
  public static function getAllHeaders() {
totten's avatar
totten committed
1128 1129 1130 1131 1132 1133
    if (function_exists('getallheaders')) {
      return getallheaders();
    }

    // emulate get all headers
    // http://www.php.net/manual/en/function.getallheaders.php#66335
1134
    $headers = [];
totten's avatar
totten committed
1135 1136 1137 1138 1139
    foreach ($_SERVER as $name => $value) {
      if (substr($name, 0, 5) == 'HTTP_') {
        $headers[str_replace(' ',
          '-',
          ucwords(strtolower(str_replace('_',
totten's avatar
totten committed
1140 1141 1142 1143
              ' ',
              substr($name, 5)
            )
          ))
totten's avatar
totten committed
1144 1145 1146 1147 1148 1149
        )] = $value;
      }
    }
    return $headers;
  }

eileenmcnaugton's avatar
eileenmcnaugton committed
1150 1151 1152 1153 1154
  /**
   * Get request headers.
   *
   * @return array|false
   */
1155
  public static function getRequestHeaders() {
totten's avatar
totten committed
1156 1157 1158 1159 1160 1161 1162 1163 1164
    if (function_exists('apache_request_headers')) {
      return apache_request_headers();
    }
    else {
      return $_SERVER;
    }
  }

  /**
1165 1166 1167 1168
   * Determine whether this is an SSL request.
   *
   * Note that we inline this function in install/civicrm.php, so if you change
   * this function, please go and change the code in the install script as well.
totten's avatar
totten committed
1169
   */
totten's avatar
totten committed
1170
  public static function isSSL() {
1171
    return (isset($_SERVER['HTTPS']) &&
totten's avatar
totten committed
1172
        !empty($_SERVER['HTTPS']) &&
1173
        strtolower($_SERVER['HTTPS']) != 'off') ? TRUE : FALSE;
totten's avatar
totten committed
1174 1175
  }

eileenmcnaugton's avatar
eileenmcnaugton committed
1176 1177 1178 1179 1180 1181 1182
  /**
   * Redirect to SSL.
   *
   * @param bool|FALSE $abort
   *
   * @throws \Exception
   */
1183
  public static function redirectToSSL($abort = FALSE) {
totten's avatar
totten committed
1184 1185
    $config = CRM_Core_Config::singleton();
    $req_headers = self::getRequestHeaders();
1186
    // FIXME: Shouldn't the X-Forwarded-Proto check be part of CRM_Utils_System::isSSL()?
1187
    if (Civi::settings()->get('enableSSL') &&
totten's avatar
totten committed
1188 1189 1190 1191 1192
      !self::isSSL() &&
      strtolower(CRM_Utils_Array::value('X_FORWARDED_PROTO', $req_headers)) != 'https'
    ) {
      // ensure that SSL is enabled on a civicrm url (for cookie reasons etc)
      $url = "https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
1193 1194
      // @see https://lab.civicrm.org/dev/core/issues/425 if you're seeing this message.
      Civi::log()->warning('CiviCRM thinks site is not SSL, redirecting to {url}', ['url' => $url]);
totten's avatar
totten committed
1195 1196 1197 1198 1199
      if (!self::checkURL($url, TRUE)) {
        if ($abort) {
          CRM_Core_Error::fatal('HTTPS is not set up on this machine');
        }
        else {
totten's avatar
totten committed
1200
          CRM_Core_Session::setStatus(ts('HTTPS is not set up on this machine'), ts('Warning'), 'alert');
totten's avatar
totten committed
1201 1202 1203 1204 1205 1206 1207 1208 1209
          // admin should be the only one following this
          // since we dont want the user stuck in a bad place
          return;
        }
      }
      CRM_Utils_System::redirect($url);
    }
  }

1210
  /**
pratik.joshi's avatar
pratik.joshi committed
1211 1212
   * Get logged in user's IP address.
   *
1213 1214 1215
   * Get IP address from HTTP REMOTE_ADDR header. If the CMS is Drupal then use
   * the Drupal function as this also handles reverse proxies (based on proper
   * configuration in settings.php)
pratik.joshi's avatar
pratik.joshi committed
1216
   *
1217 1218 1219 1220 1221
   * @param bool $strictIPV4
   *   (optional) Whether to return only IPv4 addresses.
   *
   * @return string
   *   IP address of logged in user.
pratik.joshi's avatar
pratik.joshi committed
1222
   */
1223
  public static function ipAddress($strictIPV4 = TRUE) {
totten's avatar
totten committed
1224 1225 1226
    $address = CRM_Utils_Array::value('REMOTE_ADDR', $_SERVER);

    $config = CRM_Core_Config::singleton();
1227
    if ($config->userSystem->is_drupal && function_exists('ip_address')) {
1228
      // drupal function handles the server being behind a proxy securely. We still have legacy ipn methods
1229
      // that reach this point without bootstrapping hence the check that the fn exists
totten's avatar
totten committed
1230
      $address = ip_address();
totten's avatar
totten committed
1231 1232 1233 1234 1235 1236 1237
    }

    // hack for safari
    if ($address == '::1') {
      $address = '127.0.0.1';
    }

pratik.joshi's avatar
pratik.joshi committed
1238 1239 1240 1241 1242 1243 1244 1245