Compare commits
	
		
			328 Commits
		
	
	
		
			master
			...
			blender-tw
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | bced48185b | ||
|   | 9da2ec1e8e | ||
|   | 1307f0ebbe | ||
| 5ba0687123 | |||
| fef207bf00 | |||
| 5ef3552549 | |||
| 0f6ca4be21 | |||
| 4653348b13 | |||
| 6826284108 | |||
| c93fdca80d | |||
| 432b6d61a6 | |||
| 4c2b7d7573 | |||
| d8e25a6226 | |||
| db6f4eaab1 | |||
| 4555f18c41 | |||
| 788d8a8b98 | |||
| 55f22f74f5 | |||
| de2f7e857d | |||
| 7c869ab033 | |||
| f9c680ddc0 | |||
| 8efe191897 | |||
| 8d6f1a6128 | |||
| 81bae35d2f | |||
| fc5744c7f0 | |||
| 7aea82d526 | |||
| cb29054e66 | |||
| 0416648b39 | |||
| 01fea69352 | |||
| c7a2133639 | |||
| a162f188c2 | |||
| 2b54804c7b | |||
| a8fef30871 | |||
| 9dc4d903c7 | |||
| b8b18995c5 | |||
| 971b397133 | |||
| aee7dc94bc | |||
| feb8db1212 | |||
| f9f7f005f2 | |||
| 2321a886ae | |||
| 72858c6746 | |||
| 79ca5a3026 | |||
| 3d37998174 | |||
| ce29e40b57 | |||
| ef26d6afdc | |||
| dc84bc9391 | |||
| 321eb18cd7 | |||
| 6936b8c4f3 | |||
| 26f43ee9b8 | |||
| e19d318af9 | |||
| dfc7f764de | |||
| 331b4cc42c | |||
| 674d537f91 | |||
| a442a019d9 | |||
| 774a554405 | |||
| 90f56e07ff | |||
| 4b428ffb8a | |||
| bcd0caecd1 | |||
| 78c32afa4e | |||
| f3302fe1b9 | |||
| 7eba06c43d | |||
| 713f8e2c6a | |||
| ea0100310b | |||
| 4332bfe537 | |||
| 25ce2bdd92 | |||
| dc17ef4f7c | |||
| 8097508909 | |||
| 8addc33734 | |||
| 990136a337 | |||
| d14b204aae | |||
| 4067829d2c | |||
| 8bc4c8c485 | |||
| bf8aec2a14 | |||
| ace2787868 | |||
| 08dde1da13 | |||
| 4cde7a4b06 | |||
| 42ce8a82af | |||
| 6d6e446f6f | |||
| 26bf667fec | |||
| 18b9c9b0b1 | |||
| f78ca8833b | |||
| 19337de8f0 | |||
| c2a4fbc98c | |||
| 87c0e13efd | |||
| 3442097b31 | |||
| c7764ed1d0 | |||
| 6e39022af9 | |||
| d88716feba | |||
| 678347d836 | |||
| 890ccea846 | |||
| 405525357d | |||
| 3eee2a3311 | |||
| 55d1f0081d | |||
| ea73d9bf86 | |||
| 3eab15180c | |||
| 2210be45fe | |||
| 5bdc3dfad8 | |||
| c22440fe30 | |||
| b41844290b | |||
| 32a3440156 | |||
| 630eb3c143 | |||
| 534b628ad2 | |||
| 8a463524b4 | |||
| b50eefbd5b | |||
| 3a2ebeca11 | |||
| 95f7da61a9 | |||
| 0d04beb777 | |||
| 7ab26ff7af | |||
| 6800fc6103 | |||
| 9077c1f24f | |||
| 2327f2af6f | |||
| 41fff9c930 | |||
| cfcef40c65 | |||
| 29d158a3bc | |||
| 411cf324dd | |||
| 52230a803f | |||
| ddf6f275db | |||
| 8c65ee54d0 | |||
| d07d44fcc7 | |||
| 772d59f0ab | |||
| f7da3f88f9 | |||
| e4f6cf9993 | |||
| dd34c93656 | |||
| 320ffa2308 | |||
| b5e7136f68 | |||
| e2b7664451 | |||
| 725ad07e6f | |||
| 75cbd69d85 | |||
| a9a71ef011 | |||
| 362ba36695 | |||
| 38d687ede2 | |||
| 78cd1cf494 | |||
| 0b990d42cd | |||
| 74571de5b6 | |||
| 01ce61f6e2 | |||
| 09c9aa078d | |||
| 73441d690e | |||
| afdc21ee04 | |||
| fc317b7f28 | |||
| b3f2b7faf4 | |||
| 7ea70dcb53 | |||
| d0b90fdef7 | |||
| 3c05d31d2e | |||
| 11f5eb21af | |||
| c90665645e | |||
| fb3c71820b | |||
| e533774ac0 | |||
| 647f8de9bb | |||
| 599bf57cd8 | |||
| 50c506ea2d | |||
| 4cc7990b1a | |||
| 4ef5fdefb2 | |||
| a018837214 | |||
| e74f0e1558 | |||
| fd882b5167 | |||
| 15bda62b89 | |||
| 5e4ce96266 | |||
| dbd2f0d06f | |||
| c87e657412 | |||
| 6275e3f08f | |||
| bc2e4a2452 | |||
| ba7d9fb724 | |||
| c50ab96abb | |||
| fa68c6a0fa | |||
| d1b60fec48 | |||
| 99a839605c | |||
| 08f5b3e920 | |||
| 87e0a26d24 | |||
| 91c07e9f99 | |||
| 9b2b75e4b4 | |||
| 77c913c70a | |||
| 1a1bea527c | |||
| 46f8f6d90d | |||
| dbad3b6489 | |||
| 1821cedbc4 | |||
| 1448eafd64 | |||
| 8f4047b36a | |||
| 2683fde42a | |||
| 02522d877d | |||
| 769ffa2982 | |||
| 33eed14141 | |||
| a0bd47a012 | |||
| 8c14ef97bf | |||
| 5bdfbe8250 | |||
| f2319c9689 | |||
| c3138c6497 | |||
| 56e8308621 | |||
| cb6a8756b6 | |||
| e7242143a0 | |||
| c12898b63a | |||
| 9453f29deb | |||
| 9093816113 | |||
| 3471c1b87e | |||
| 30f0de9427 | |||
| 5278e94566 | |||
| 08b1e9d764 | |||
| 30427e0626 | |||
| 752a5b9bc3 | |||
| a5390d5461 | |||
| a02fa19d5a | |||
| f88bdcd4ff | |||
| b98f8a570c | |||
| 644070340d | |||
| 93fecfe7e9 | |||
| 53bb3ac361 | |||
| 137df8c6d3 | |||
| 9863285bf0 | |||
| a392b4a14f | |||
| d03569e908 | |||
| b1097e9c61 | |||
| 5bda6bf48b | |||
| a03cdacd9e | |||
| c3fe33f5a0 | |||
| 5d823ff1a7 | |||
| 63df9dd1f4 | |||
| 0c80c9fead | |||
| b8d42a7cc6 | |||
| 4e7dc30fea | |||
| 99c68b1091 | |||
| d48979091e | |||
| 785caa5b6c | |||
| 261d7331f4 | |||
| 28bd8409df | |||
| 47008a157c | |||
| fc1e8681ef | |||
| 17085b4379 | |||
| dd6eefcd9c | |||
| ae2b0e63d6 | |||
| bcbae097de | |||
| b59dca4eeb | |||
| 8098798fa9 | |||
| d8d0e2c99f | |||
| 214233f82f | |||
| eebe82c990 | |||
| 5b73071d64 | |||
| cd04de7f72 | |||
| cf4b1317e1 | |||
| 10e353b090 | |||
| 81bd756ec6 | |||
| 9527d8aafd | |||
| e35bc58e0e | |||
| bd0f9fd913 | |||
| 44957f7806 | |||
| 7ff26eb95d | |||
| 1f2471fa67 | |||
| 7a5588a408 | |||
| ac03ae737f | |||
| f9db95f555 | |||
| 2bc045a997 | |||
| 531b0fa529 | |||
| 6b1ed4fb9b | |||
| 645a6a9f54 | |||
| 88cd291b0f | |||
| 76e4aaf429 | |||
| 365f8ad981 | |||
| 0af08f9469 | |||
| 23ce658af7 | |||
| 0a54474f98 | |||
| 93ca9ed3a8 | |||
| d110fdccc2 | |||
| 4197d983bf | |||
| d23883b799 | |||
| 675d2ac374 | |||
| 5c86735ab7 | |||
| 566274dcac | |||
| de2814545b | |||
| 4ea26f22c4 | |||
| 458bc7f921 | |||
| 3839698e4a | |||
| b403863e62 | |||
| 246b73f922 | |||
| 6da7f462a9 | |||
| 90874b8b00 | |||
| 7e874a33fb | |||
| bbe357e5c0 | |||
| 428cdae3cf | |||
| 1c689a6b24 | |||
| 55972ef2c1 | |||
| 966687d1ab | |||
| 6a86144adf | |||
| 57765e88c0 | |||
| 60cddad047 | |||
| a77a33dc50 | |||
| 405a07e638 | |||
| 75f9ce9d77 | |||
| 259588e747 | |||
| 0e8b746fb0 | |||
| 753917c10c | |||
| 39f7364f25 | |||
| 8d7de5fcb6 | |||
| 01398f762b | |||
| e12d2efe81 | |||
| 5c7055d840 | |||
| 562c7aa126 | |||
| 5994088e50 | |||
| 9edbe22e19 | |||
| 7fabfff397 | |||
| 356a2fe0e8 | |||
| 54193ebb15 | |||
| 2b4bb9cddd | |||
| 2758907517 | |||
| a7f641eee2 | |||
| 9799ceff80 | |||
| 26e86c2171 | |||
| f6c293fb4d | |||
| 5abc5cf9f8 | |||
| 41a820fdf0 | |||
| 2c8b09da21 | |||
| a7ff526360 | |||
| 1a8148be97 | |||
| 199dd85140 | |||
| f6c44fdb03 | |||
| 1aade72cb1 | |||
| 121c9d308b | |||
| d60a47ab5b | |||
| 8af9b8796f | |||
| 7dd7673fb3 | |||
| aeaf0902e1 | |||
| 498842475c | |||
| 755177508e | |||
| 5b49fd93e2 | |||
| 968ea35b03 | |||
| 97ed90b82a | |||
| ba34aa535a | |||
| e56041da3b | |||
| b19fbc39af | |||
| 1036d419fa | |||
| 0011789443 | |||
| ff7d71fb87 | 
| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|   "phabricator.uri": "https://secure.phabricator.com/", | ||||
|   "phabricator.uri": "https://developer.blender.org/", | ||||
|   "load": ["src/"], | ||||
|   "history.immutable": false | ||||
| } | ||||
|   | ||||
							
								
								
									
										4
									
								
								.arclint
									
									
									
									
									
								
							
							
						
						| @@ -64,13 +64,13 @@ | ||||
|     "text": { | ||||
|       "type": "text", | ||||
|       "exclude": [ | ||||
|         "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))" | ||||
|         "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json))" | ||||
|       ] | ||||
|     }, | ||||
|     "text-without-length": { | ||||
|       "type": "text", | ||||
|       "include": [ | ||||
|         "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))" | ||||
|         "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json))" | ||||
|       ], | ||||
|       "severity": { | ||||
|         "3": "disabled" | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -40,3 +40,6 @@ | ||||
| # Places for users to add custom resources. | ||||
| /resources/cows/custom/* | ||||
| /resources/figlet/custom/* | ||||
|  | ||||
| # blender migration files | ||||
| migration/dump/ | ||||
|   | ||||
							
								
								
									
										722
									
								
								externals/amazon-ses/ses.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,722 @@ | ||||
| <?php | ||||
| /** | ||||
| * | ||||
| * Copyright (c) 2011, Dan Myers. | ||||
| * Parts copyright (c) 2008, Donovan Schonknecht. | ||||
| * All rights reserved. | ||||
| * | ||||
| * Redistribution and use in source and binary forms, with or without | ||||
| * modification, are permitted provided that the following conditions are met: | ||||
| * | ||||
| * - Redistributions of source code must retain the above copyright notice, | ||||
| *   this list of conditions and the following disclaimer. | ||||
| * - Redistributions in binary form must reproduce the above copyright | ||||
| *   notice, this list of conditions and the following disclaimer in the | ||||
| *   documentation and/or other materials provided with the distribution. | ||||
| * | ||||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||||
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
| * POSSIBILITY OF SUCH DAMAGE. | ||||
| * | ||||
| * This is a modified BSD license (the third clause has been removed). | ||||
| * The BSD license may be found here: | ||||
| * http://www.opensource.org/licenses/bsd-license.php | ||||
| * | ||||
| * Amazon Simple Email Service is a trademark of Amazon.com, Inc. or its affiliates. | ||||
| * | ||||
| * SimpleEmailService is based on Donovan Schonknecht's Amazon S3 PHP class, found here: | ||||
| * http://undesigned.org.za/2007/10/22/amazon-s3-php-class | ||||
| * | ||||
| */ | ||||
|  | ||||
| /** | ||||
| * Amazon SimpleEmailService PHP class | ||||
| * | ||||
| * @link http://sourceforge.net/projects/php-aws-ses/ | ||||
| * version 0.8.1 | ||||
| * | ||||
| */ | ||||
| class SimpleEmailService | ||||
| { | ||||
|   protected $__accessKey; // AWS Access key | ||||
|   protected $__secretKey; // AWS Secret key | ||||
|   protected $__host; | ||||
|  | ||||
|   public function getAccessKey() { return $this->__accessKey; } | ||||
|   public function getSecretKey() { return $this->__secretKey; } | ||||
|   public function getHost() { return $this->__host; } | ||||
|  | ||||
|   protected $__verifyHost = 1; | ||||
|   protected $__verifyPeer = 1; | ||||
|  | ||||
|   // verifyHost and verifyPeer determine whether curl verifies ssl certificates. | ||||
|   // It may be necessary to disable these checks on certain systems. | ||||
|   // These only have an effect if SSL is enabled. | ||||
|   public function verifyHost() { return $this->__verifyHost; } | ||||
|   public function enableVerifyHost($enable = true) { $this->__verifyHost = $enable; } | ||||
|  | ||||
|   public function verifyPeer() { return $this->__verifyPeer; } | ||||
|   public function enableVerifyPeer($enable = true) { $this->__verifyPeer = $enable; } | ||||
|  | ||||
|   // If you use exceptions, errors will be communicated by throwing a | ||||
|   // SimpleEmailServiceException. By default, they will be trigger_error()'d. | ||||
|   protected $__useExceptions = 0; | ||||
|   public function useExceptions() { return $this->__useExceptions; } | ||||
|   public function enableUseExceptions($enable = true) { $this->__useExceptions = $enable; } | ||||
|  | ||||
|   /** | ||||
|   * Constructor | ||||
|   * | ||||
|   * @param string $accessKey Access key | ||||
|   * @param string $secretKey Secret key | ||||
|   * @return void | ||||
|   */ | ||||
|   public function __construct($accessKey = null, $secretKey = null, $host = 'email.us-east-1.amazonaws.com') { | ||||
|     if (!function_exists('simplexml_load_string')) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'The PHP SimpleXML extension is not available, but this '. | ||||
|           'extension is required to send mail via Amazon SES, because '. | ||||
|           'Amazon SES returns API responses in XML format. Install or '. | ||||
|           'enable the SimpleXML extension.')); | ||||
|     } | ||||
|  | ||||
|     // Catch mistakes with reading the wrong column out of the SES | ||||
|     // documentation. See T10728. | ||||
|     if (preg_match('(-smtp)', $host)) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Amazon SES is not configured correctly: the configured SES '. | ||||
|           'endpoint ("%s") is an SMTP endpoint. Instead, use an API (HTTPS) '. | ||||
|           'endpoint.', | ||||
|           $host)); | ||||
|     } | ||||
|  | ||||
|     if ($accessKey !== null && $secretKey !== null) { | ||||
|       $this->setAuth($accessKey, $secretKey); | ||||
|     } | ||||
|  | ||||
|     $this->__host = $host; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Set AWS access key and secret key | ||||
|   * | ||||
|   * @param string $accessKey Access key | ||||
|   * @param string $secretKey Secret key | ||||
|   * @return void | ||||
|   */ | ||||
|   public function setAuth($accessKey, $secretKey) { | ||||
|     $this->__accessKey = $accessKey; | ||||
|     $this->__secretKey = $secretKey; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Lists the email addresses that have been verified and can be used as the 'From' address | ||||
|   * | ||||
|   * @return An array containing two items: a list of verified email addresses, and the request id. | ||||
|   */ | ||||
|   public function listVerifiedEmailAddresses() { | ||||
|     $rest = new SimpleEmailServiceRequest($this, 'GET'); | ||||
|     $rest->setParameter('Action', 'ListVerifiedEmailAddresses'); | ||||
|  | ||||
|     $rest = $rest->getResponse(); | ||||
|  | ||||
|     $response = array(); | ||||
|     if(!isset($rest->body)) { | ||||
|       return $response; | ||||
|     } | ||||
|  | ||||
|     $addresses = array(); | ||||
|     foreach($rest->body->ListVerifiedEmailAddressesResult->VerifiedEmailAddresses->member as $address) { | ||||
|       $addresses[] = (string)$address; | ||||
|     } | ||||
|  | ||||
|     $response['Addresses'] = $addresses; | ||||
|     $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; | ||||
|  | ||||
|     return $response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Requests verification of the provided email address, so it can be used | ||||
|   * as the 'From' address when sending emails through SimpleEmailService. | ||||
|   * | ||||
|   * After submitting this request, you should receive a verification email | ||||
|   * from Amazon at the specified address containing instructions to follow. | ||||
|   * | ||||
|   * @param string email The email address to get verified | ||||
|   * @return The request id for this request. | ||||
|   */ | ||||
|   public function verifyEmailAddress($email) { | ||||
|     $rest = new SimpleEmailServiceRequest($this, 'POST'); | ||||
|     $rest->setParameter('Action', 'VerifyEmailAddress'); | ||||
|     $rest->setParameter('EmailAddress', $email); | ||||
|  | ||||
|     $rest = $rest->getResponse(); | ||||
|  | ||||
|     $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; | ||||
|     return $response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Removes the specified email address from the list of verified addresses. | ||||
|   * | ||||
|   * @param string email The email address to remove | ||||
|   * @return The request id for this request. | ||||
|   */ | ||||
|   public function deleteVerifiedEmailAddress($email) { | ||||
|     $rest = new SimpleEmailServiceRequest($this, 'DELETE'); | ||||
|     $rest->setParameter('Action', 'DeleteVerifiedEmailAddress'); | ||||
|     $rest->setParameter('EmailAddress', $email); | ||||
|  | ||||
|     $rest = $rest->getResponse(); | ||||
|  | ||||
|     $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; | ||||
|     return $response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Retrieves information on the current activity limits for this account. | ||||
|   * See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendQuota.html | ||||
|   * | ||||
|   * @return An array containing information on this account's activity limits. | ||||
|   */ | ||||
|   public function getSendQuota() { | ||||
|     $rest = new SimpleEmailServiceRequest($this, 'GET'); | ||||
|     $rest->setParameter('Action', 'GetSendQuota'); | ||||
|  | ||||
|     $rest = $rest->getResponse(); | ||||
|  | ||||
|     $response = array(); | ||||
|     if(!isset($rest->body)) { | ||||
|       return $response; | ||||
|     } | ||||
|  | ||||
|     $response['Max24HourSend'] = (string)$rest->body->GetSendQuotaResult->Max24HourSend; | ||||
|     $response['MaxSendRate'] = (string)$rest->body->GetSendQuotaResult->MaxSendRate; | ||||
|     $response['SentLast24Hours'] = (string)$rest->body->GetSendQuotaResult->SentLast24Hours; | ||||
|     $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; | ||||
|  | ||||
|     return $response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Retrieves statistics for the last two weeks of activity on this account. | ||||
|   * See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendStatistics.html | ||||
|   * | ||||
|   * @return An array of activity statistics.  Each array item covers a 15-minute period. | ||||
|   */ | ||||
|   public function getSendStatistics() { | ||||
|     $rest = new SimpleEmailServiceRequest($this, 'GET'); | ||||
|     $rest->setParameter('Action', 'GetSendStatistics'); | ||||
|  | ||||
|     $rest = $rest->getResponse(); | ||||
|  | ||||
|     $response = array(); | ||||
|     if(!isset($rest->body)) { | ||||
|       return $response; | ||||
|     } | ||||
|  | ||||
|     $datapoints = array(); | ||||
|     foreach($rest->body->GetSendStatisticsResult->SendDataPoints->member as $datapoint) { | ||||
|       $p = array(); | ||||
|       $p['Bounces'] = (string)$datapoint->Bounces; | ||||
|       $p['Complaints'] = (string)$datapoint->Complaints; | ||||
|       $p['DeliveryAttempts'] = (string)$datapoint->DeliveryAttempts; | ||||
|       $p['Rejects'] = (string)$datapoint->Rejects; | ||||
|       $p['Timestamp'] = (string)$datapoint->Timestamp; | ||||
|  | ||||
|       $datapoints[] = $p; | ||||
|     } | ||||
|  | ||||
|     $response['SendDataPoints'] = $datapoints; | ||||
|     $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; | ||||
|  | ||||
|     return $response; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   public function sendRawEmail($raw) { | ||||
|     $rest = new SimpleEmailServiceRequest($this, 'POST'); | ||||
|     $rest->setParameter('Action', 'SendRawEmail'); | ||||
|     $rest->setParameter('RawMessage.Data', base64_encode($raw)); | ||||
|  | ||||
|     $rest = $rest->getResponse(); | ||||
|  | ||||
|     $response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId; | ||||
|     $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; | ||||
|     return $response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Given a SimpleEmailServiceMessage object, submits the message to the service for sending. | ||||
|   * | ||||
|   * @return An array containing the unique identifier for this message and a separate request id. | ||||
|   *         Returns false if the provided message is missing any required fields. | ||||
|   */ | ||||
|   public function sendEmail($sesMessage) { | ||||
|     if(!$sesMessage->validate()) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     $rest = new SimpleEmailServiceRequest($this, 'POST'); | ||||
|     $rest->setParameter('Action', 'SendEmail'); | ||||
|  | ||||
|     $i = 1; | ||||
|     foreach($sesMessage->to as $to) { | ||||
|       $rest->setParameter('Destination.ToAddresses.member.'.$i, $to); | ||||
|       $i++; | ||||
|     } | ||||
|  | ||||
|     if(is_array($sesMessage->cc)) { | ||||
|       $i = 1; | ||||
|       foreach($sesMessage->cc as $cc) { | ||||
|         $rest->setParameter('Destination.CcAddresses.member.'.$i, $cc); | ||||
|         $i++; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if(is_array($sesMessage->bcc)) { | ||||
|       $i = 1; | ||||
|       foreach($sesMessage->bcc as $bcc) { | ||||
|         $rest->setParameter('Destination.BccAddresses.member.'.$i, $bcc); | ||||
|         $i++; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if(is_array($sesMessage->replyto)) { | ||||
|       $i = 1; | ||||
|       foreach($sesMessage->replyto as $replyto) { | ||||
|         $rest->setParameter('ReplyToAddresses.member.'.$i, $replyto); | ||||
|         $i++; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $rest->setParameter('Source', $sesMessage->from); | ||||
|  | ||||
|     if($sesMessage->returnpath != null) { | ||||
|       $rest->setParameter('ReturnPath', $sesMessage->returnpath); | ||||
|     } | ||||
|  | ||||
|     if($sesMessage->subject != null && strlen($sesMessage->subject) > 0) { | ||||
|       $rest->setParameter('Message.Subject.Data', $sesMessage->subject); | ||||
|       if($sesMessage->subjectCharset != null && strlen($sesMessage->subjectCharset) > 0) { | ||||
|         $rest->setParameter('Message.Subject.Charset', $sesMessage->subjectCharset); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     if($sesMessage->messagetext != null && strlen($sesMessage->messagetext) > 0) { | ||||
|       $rest->setParameter('Message.Body.Text.Data', $sesMessage->messagetext); | ||||
|       if($sesMessage->messageTextCharset != null && strlen($sesMessage->messageTextCharset) > 0) { | ||||
|         $rest->setParameter('Message.Body.Text.Charset', $sesMessage->messageTextCharset); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if($sesMessage->messagehtml != null && strlen($sesMessage->messagehtml) > 0) { | ||||
|       $rest->setParameter('Message.Body.Html.Data', $sesMessage->messagehtml); | ||||
|       if($sesMessage->messageHtmlCharset != null && strlen($sesMessage->messageHtmlCharset) > 0) { | ||||
|         $rest->setParameter('Message.Body.Html.Charset', $sesMessage->messageHtmlCharset); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $rest = $rest->getResponse(); | ||||
|  | ||||
|     $response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId; | ||||
|     $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; | ||||
|     return $response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Trigger an error message | ||||
|   * | ||||
|   * @internal Used by member functions to output errors | ||||
|   * @param array $error Array containing error information | ||||
|   * @return string | ||||
|   */ | ||||
|   public function __triggerError($functionname, $error) | ||||
|   { | ||||
|     if($error == false) { | ||||
|       $message = sprintf("SimpleEmailService::%s(): Encountered an error, but no description given", $functionname); | ||||
|     } | ||||
|     else if(isset($error['curl']) && $error['curl']) | ||||
|     { | ||||
|       $message = sprintf("SimpleEmailService::%s(): %s %s", $functionname, $error['code'], $error['message']); | ||||
|     } | ||||
|     else if(isset($error['Error'])) | ||||
|     { | ||||
|       $e = $error['Error']; | ||||
|       $message = sprintf("SimpleEmailService::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname, $e['Type'], $e['Code'], $e['Message'], $error['RequestId']); | ||||
|     } | ||||
|  | ||||
|     if ($this->useExceptions()) { | ||||
|       throw new SimpleEmailServiceException($message); | ||||
|     } else { | ||||
|       trigger_error($message, E_USER_WARNING); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Callback handler for 503 retries. | ||||
|   * | ||||
|   * @internal Used by SimpleDBRequest to call the user-specified callback, if set | ||||
|   * @param $attempt The number of failed attempts so far | ||||
|   * @return The retry delay in microseconds, or 0 to stop retrying. | ||||
|   */ | ||||
|   public function __executeServiceTemporarilyUnavailableRetryDelay($attempt) | ||||
|   { | ||||
|     if(is_callable($this->__serviceUnavailableRetryDelayCallback)) { | ||||
|       $callback = $this->__serviceUnavailableRetryDelayCallback; | ||||
|       return $callback($attempt); | ||||
|     } | ||||
|     return 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| final class SimpleEmailServiceRequest | ||||
| { | ||||
|   private $ses, $verb, $parameters = array(); | ||||
|   public $response; | ||||
|  | ||||
|   /** | ||||
|   * Constructor | ||||
|   * | ||||
|   * @param string $ses The SimpleEmailService object making this request | ||||
|   * @param string $action action | ||||
|   * @param string $verb HTTP verb | ||||
|   * @return mixed | ||||
|   */ | ||||
|   function __construct($ses, $verb) { | ||||
|     $this->ses = $ses; | ||||
|     $this->verb = $verb; | ||||
|     $this->response = new STDClass; | ||||
|     $this->response->error = false; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Set request parameter | ||||
|   * | ||||
|   * @param string  $key Key | ||||
|   * @param string  $value Value | ||||
|   * @param boolean $replace Whether to replace the key if it already exists (default true) | ||||
|   * @return void | ||||
|   */ | ||||
|   public function setParameter($key, $value, $replace = true) { | ||||
|     if(!$replace && isset($this->parameters[$key])) | ||||
|     { | ||||
|       $temp = (array)($this->parameters[$key]); | ||||
|       $temp[] = $value; | ||||
|       $this->parameters[$key] = $temp; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       $this->parameters[$key] = $value; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Get the response | ||||
|   * | ||||
|   * @return object | false | ||||
|   */ | ||||
|   public function getResponse() { | ||||
|  | ||||
|     $params = array(); | ||||
|     foreach ($this->parameters as $var => $value) | ||||
|     { | ||||
|       if(is_array($value)) | ||||
|       { | ||||
|         foreach($value as $v) | ||||
|         { | ||||
|           $params[] = $var.'='.$this->__customUrlEncode($v); | ||||
|         } | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         $params[] = $var.'='.$this->__customUrlEncode($value); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     sort($params, SORT_STRING); | ||||
|  | ||||
|     // must be in format 'Sun, 06 Nov 1994 08:49:37 GMT' | ||||
|     $date = gmdate('D, d M Y H:i:s e'); | ||||
|  | ||||
|     $query = implode('&', $params); | ||||
|  | ||||
|     $headers = array(); | ||||
|     $headers[] = 'Date: '.$date; | ||||
|     $headers[] = 'Host: '.$this->ses->getHost(); | ||||
|  | ||||
|     $auth = 'AWS3-HTTPS AWSAccessKeyId='.$this->ses->getAccessKey(); | ||||
|     $auth .= ',Algorithm=HmacSHA256,Signature='.$this->__getSignature($date); | ||||
|     $headers[] = 'X-Amzn-Authorization: '.$auth; | ||||
|  | ||||
|     $url = 'https://'.$this->ses->getHost().'/'; | ||||
|  | ||||
|     // Basic setup | ||||
|     $curl = curl_init(); | ||||
|     curl_setopt($curl, CURLOPT_USERAGENT, 'SimpleEmailService/php'); | ||||
|  | ||||
|     curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, ($this->ses->verifyHost() ? 2 : 0)); | ||||
|     curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ($this->ses->verifyPeer() ? 1 : 0)); | ||||
|  | ||||
|     // Request types | ||||
|     switch ($this->verb) { | ||||
|       case 'GET': | ||||
|         $url .= '?'.$query; | ||||
|         break; | ||||
|       case 'POST': | ||||
|         curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); | ||||
|         curl_setopt($curl, CURLOPT_POSTFIELDS, $query); | ||||
|         $headers[] = 'Content-Type: application/x-www-form-urlencoded'; | ||||
|       break; | ||||
|       case 'DELETE': | ||||
|         $url .= '?'.$query; | ||||
|         curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); | ||||
|       break; | ||||
|       default: break; | ||||
|     } | ||||
|     curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); | ||||
|     curl_setopt($curl, CURLOPT_HEADER, false); | ||||
|  | ||||
|     curl_setopt($curl, CURLOPT_URL, $url); | ||||
|     curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); | ||||
|     curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); | ||||
|     curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); | ||||
|  | ||||
|     // Execute, grab errors | ||||
|     if (!curl_exec($curl)) { | ||||
|       throw new SimpleEmailServiceException( | ||||
|         pht( | ||||
|           'Encountered an error while making an HTTP request to Amazon SES '. | ||||
|           '(cURL Error #%d): %s', | ||||
|           curl_errno($curl), | ||||
|           curl_error($curl))); | ||||
|     } | ||||
|  | ||||
|     $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); | ||||
|     if ($this->response->code != 200) { | ||||
|       throw new SimpleEmailServiceException( | ||||
|         pht( | ||||
|           'Unexpected HTTP status while making request to Amazon SES: '. | ||||
|           'expected 200, got %s.', | ||||
|           $this->response->code)); | ||||
|     } | ||||
|  | ||||
|     @curl_close($curl); | ||||
|  | ||||
|     // Parse body into XML | ||||
|     if ($this->response->error === false && isset($this->response->body)) { | ||||
|       $this->response->body = simplexml_load_string($this->response->body); | ||||
|  | ||||
|       // Grab SES errors | ||||
|       if (!in_array($this->response->code, array(200, 201, 202, 204)) | ||||
|         && isset($this->response->body->Error)) { | ||||
|         $error = $this->response->body->Error; | ||||
|         $output = array(); | ||||
|         $output['curl'] = false; | ||||
|         $output['Error'] = array(); | ||||
|         $output['Error']['Type'] = (string)$error->Type; | ||||
|         $output['Error']['Code'] = (string)$error->Code; | ||||
|         $output['Error']['Message'] = (string)$error->Message; | ||||
|         $output['RequestId'] = (string)$this->response->body->RequestId; | ||||
|  | ||||
|         $this->response->error = $output; | ||||
|         unset($this->response->body); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $this->response; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * CURL write callback | ||||
|   * | ||||
|   * @param resource &$curl CURL resource | ||||
|   * @param string &$data Data | ||||
|   * @return integer | ||||
|   */ | ||||
|   private function __responseWriteCallback(&$curl, &$data) { | ||||
|     if(!isset($this->response->body)) $this->response->body = ''; | ||||
|     $this->response->body .= $data; | ||||
|     return strlen($data); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Contributed by afx114 | ||||
|   * URL encode the parameters as per http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?Query_QueryAuth.html | ||||
|   * PHP's rawurlencode() follows RFC 1738, not RFC 3986 as required by Amazon. The only difference is the tilde (~), so convert it back after rawurlencode | ||||
|   * See: http://www.morganney.com/blog/API/AWS-Product-Advertising-API-Requires-a-Signed-Request.php | ||||
|   * | ||||
|   * @param string $var String to encode | ||||
|   * @return string | ||||
|   */ | ||||
|   private function __customUrlEncode($var) { | ||||
|     return str_replace('%7E', '~', rawurlencode($var)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Generate the auth string using Hmac-SHA256 | ||||
|   * | ||||
|   * @internal Used by SimpleDBRequest::getResponse() | ||||
|   * @param string $string String to sign | ||||
|   * @return string | ||||
|   */ | ||||
|   private function __getSignature($string) { | ||||
|     return base64_encode(hash_hmac('sha256', $string, $this->ses->getSecretKey(), true)); | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| final class SimpleEmailServiceMessage { | ||||
|  | ||||
|   // these are public for convenience only | ||||
|   // these are not to be used outside of the SimpleEmailService class! | ||||
|   public $to, $cc, $bcc, $replyto; | ||||
|   public $from, $returnpath; | ||||
|   public $subject, $messagetext, $messagehtml; | ||||
|   public $subjectCharset, $messageTextCharset, $messageHtmlCharset; | ||||
|  | ||||
|   function __construct() { | ||||
|     $to = array(); | ||||
|     $cc = array(); | ||||
|     $bcc = array(); | ||||
|     $replyto = array(); | ||||
|  | ||||
|     $from = null; | ||||
|     $returnpath = null; | ||||
|  | ||||
|     $subject = null; | ||||
|     $messagetext = null; | ||||
|     $messagehtml = null; | ||||
|  | ||||
|     $subjectCharset = null; | ||||
|     $messageTextCharset = null; | ||||
|     $messageHtmlCharset = null; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|   * addTo, addCC, addBCC, and addReplyTo have the following behavior: | ||||
|   * If a single address is passed, it is appended to the current list of addresses. | ||||
|   * If an array of addresses is passed, that array is merged into the current list. | ||||
|   */ | ||||
|   function addTo($to) { | ||||
|     if(!is_array($to)) { | ||||
|       $this->to[] = $to; | ||||
|     } | ||||
|     else { | ||||
|       $this->to = array_merge($this->to, $to); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function addCC($cc) { | ||||
|     if(!is_array($cc)) { | ||||
|       $this->cc[] = $cc; | ||||
|     } | ||||
|     else { | ||||
|       $this->cc = array_merge($this->cc, $cc); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function addBCC($bcc) { | ||||
|     if(!is_array($bcc)) { | ||||
|       $this->bcc[] = $bcc; | ||||
|     } | ||||
|     else { | ||||
|       $this->bcc = array_merge($this->bcc, $bcc); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function addReplyTo($replyto) { | ||||
|     if(!is_array($replyto)) { | ||||
|       $this->replyto[] = $replyto; | ||||
|     } | ||||
|     else { | ||||
|       $this->replyto = array_merge($this->replyto, $replyto); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function setFrom($from) { | ||||
|     $this->from = $from; | ||||
|   } | ||||
|  | ||||
|   function setReturnPath($returnpath) { | ||||
|     $this->returnpath = $returnpath; | ||||
|   } | ||||
|  | ||||
|   function setSubject($subject) { | ||||
|     $this->subject = $subject; | ||||
|   } | ||||
|  | ||||
|   function setSubjectCharset($charset) { | ||||
|     $this->subjectCharset = $charset; | ||||
|   } | ||||
|  | ||||
|   function setMessageFromString($text, $html = null) { | ||||
|     $this->messagetext = $text; | ||||
|     $this->messagehtml = $html; | ||||
|   } | ||||
|  | ||||
|   function setMessageFromFile($textfile, $htmlfile = null) { | ||||
|     if(file_exists($textfile) && is_file($textfile) && is_readable($textfile)) { | ||||
|       $this->messagetext = file_get_contents($textfile); | ||||
|     } | ||||
|     if(file_exists($htmlfile) && is_file($htmlfile) && is_readable($htmlfile)) { | ||||
|       $this->messagehtml = file_get_contents($htmlfile); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function setMessageFromURL($texturl, $htmlurl = null) { | ||||
|     $this->messagetext = file_get_contents($texturl); | ||||
|     if($htmlurl !== null) { | ||||
|       $this->messagehtml = file_get_contents($htmlurl); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function setMessageCharset($textCharset, $htmlCharset = null) { | ||||
|     $this->messageTextCharset = $textCharset; | ||||
|     $this->messageHtmlCharset = $htmlCharset; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|   * Validates whether the message object has sufficient information to submit a request to SES. | ||||
|   * This does not guarantee the message will arrive, nor that the request will succeed; | ||||
|   * instead, it makes sure that no required fields are missing. | ||||
|   * | ||||
|   * This is used internally before attempting a SendEmail or SendRawEmail request, | ||||
|   * but it can be used outside of this file if verification is desired. | ||||
|   * May be useful if e.g. the data is being populated from a form; developers can generally | ||||
|   * use this function to verify completeness instead of writing custom logic. | ||||
|   * | ||||
|   * @return boolean | ||||
|   */ | ||||
|   public function validate() { | ||||
|     if(count($this->to) == 0) | ||||
|       return false; | ||||
|     if($this->from == null || strlen($this->from) == 0) | ||||
|       return false; | ||||
|     if($this->messagetext == null) | ||||
|       return false; | ||||
|     return true; | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Thrown by SimpleEmailService when errors occur if you call | ||||
|  * enableUseExceptions(true). | ||||
|  */ | ||||
| final class SimpleEmailServiceException extends Exception { | ||||
|  | ||||
| } | ||||
							
								
								
									
										769
									
								
								externals/cldr/cldr_windows_timezones.xml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,769 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" ?> | ||||
| <!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd"> | ||||
| <!-- | ||||
| Copyright © 1991-2013 Unicode, Inc. | ||||
| CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/) | ||||
| For terms of use, see http://www.unicode.org/copyright.html | ||||
| --> | ||||
|  | ||||
| <supplementalData> | ||||
| 	<version number="$Revision$"/> | ||||
| 	<windowsZones> | ||||
| 		<mapTimezones otherVersion="7e00402" typeVersion="2016i"> | ||||
|  | ||||
| 			<!-- (UTC-12:00) International Date Line West --> | ||||
| 			<mapZone other="Dateline Standard Time" territory="001" type="Etc/GMT+12"/> | ||||
| 			<mapZone other="Dateline Standard Time" territory="ZZ" type="Etc/GMT+12"/> | ||||
|  | ||||
| 			<!-- (UTC-11:00) Coordinated Universal Time-11 --> | ||||
| 			<mapZone other="UTC-11" territory="001" type="Etc/GMT+11"/> | ||||
| 			<mapZone other="UTC-11" territory="AS" type="Pacific/Pago_Pago"/> | ||||
| 			<mapZone other="UTC-11" territory="NU" type="Pacific/Niue"/> | ||||
| 			<mapZone other="UTC-11" territory="UM" type="Pacific/Midway"/> | ||||
| 			<mapZone other="UTC-11" territory="ZZ" type="Etc/GMT+11"/> | ||||
|  | ||||
| 			<!-- (UTC-10:00) Aleutian Islands --> | ||||
| 			<mapZone other="Aleutian Standard Time" territory="001" type="America/Adak"/> | ||||
| 			<mapZone other="Aleutian Standard Time" territory="US" type="America/Adak"/> | ||||
|  | ||||
| 			<!-- (UTC-10:00) Hawaii --> | ||||
| 			<mapZone other="Hawaiian Standard Time" territory="001" type="Pacific/Honolulu"/> | ||||
| 			<mapZone other="Hawaiian Standard Time" territory="CK" type="Pacific/Rarotonga"/> | ||||
| 			<mapZone other="Hawaiian Standard Time" territory="PF" type="Pacific/Tahiti"/> | ||||
| 			<mapZone other="Hawaiian Standard Time" territory="UM" type="Pacific/Johnston"/> | ||||
| 			<mapZone other="Hawaiian Standard Time" territory="US" type="Pacific/Honolulu"/> | ||||
| 			<mapZone other="Hawaiian Standard Time" territory="ZZ" type="Etc/GMT+10"/> | ||||
|  | ||||
| 			<!-- (UTC-09:30) Marquesas Islands --> | ||||
| 			<mapZone other="Marquesas Standard Time" territory="001" type="Pacific/Marquesas"/> | ||||
| 			<mapZone other="Marquesas Standard Time" territory="PF" type="Pacific/Marquesas"/> | ||||
|  | ||||
| 			<!-- (UTC-09:00) Alaska --> | ||||
| 			<mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/> | ||||
| 			<mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Metlakatla America/Nome America/Sitka America/Yakutat"/> | ||||
|  | ||||
| 			<!-- (UTC-09:00) Coordinated Universal Time-09 --> | ||||
| 			<mapZone other="UTC-09" territory="001" type="Etc/GMT+9"/> | ||||
| 			<mapZone other="UTC-09" territory="PF" type="Pacific/Gambier"/> | ||||
| 			<mapZone other="UTC-09" territory="ZZ" type="Etc/GMT+9"/> | ||||
|  | ||||
| 			<!-- (UTC-08:00) Baja California --> | ||||
| 			<mapZone other="Pacific Standard Time (Mexico)" territory="001" type="America/Tijuana"/> | ||||
| 			<mapZone other="Pacific Standard Time (Mexico)" territory="MX" type="America/Tijuana America/Santa_Isabel"/> | ||||
|  | ||||
| 			<!-- (UTC-08:00) Coordinated Universal Time-08 --> | ||||
| 			<mapZone other="UTC-08" territory="001" type="Etc/GMT+8"/> | ||||
| 			<mapZone other="UTC-08" territory="PN" type="Pacific/Pitcairn"/> | ||||
| 			<mapZone other="UTC-08" territory="ZZ" type="Etc/GMT+8"/> | ||||
|  | ||||
| 			<!-- (UTC-08:00) Pacific Time (US & Canada) --> | ||||
| 			<mapZone other="Pacific Standard Time" territory="001" type="America/Los_Angeles"/> | ||||
| 			<mapZone other="Pacific Standard Time" territory="CA" type="America/Vancouver America/Dawson America/Whitehorse"/> | ||||
| 			<mapZone other="Pacific Standard Time" territory="US" type="America/Los_Angeles"/> | ||||
| 			<mapZone other="Pacific Standard Time" territory="ZZ" type="PST8PDT"/> | ||||
|  | ||||
| 			<!-- (UTC-07:00) Arizona --> | ||||
| 			<mapZone other="US Mountain Standard Time" territory="001" type="America/Phoenix"/> | ||||
| 			<mapZone other="US Mountain Standard Time" territory="CA" type="America/Dawson_Creek America/Creston America/Fort_Nelson"/> | ||||
| 			<mapZone other="US Mountain Standard Time" territory="MX" type="America/Hermosillo"/> | ||||
| 			<mapZone other="US Mountain Standard Time" territory="US" type="America/Phoenix"/> | ||||
| 			<mapZone other="US Mountain Standard Time" territory="ZZ" type="Etc/GMT+7"/> | ||||
|  | ||||
| 			<!-- (UTC-07:00) Chihuahua, La Paz, Mazatlan --> | ||||
| 			<mapZone other="Mountain Standard Time (Mexico)" territory="001" type="America/Chihuahua"/> | ||||
| 			<mapZone other="Mountain Standard Time (Mexico)" territory="MX" type="America/Chihuahua America/Mazatlan"/> | ||||
|  | ||||
| 			<!-- (UTC-07:00) Mountain Time (US & Canada) --> | ||||
| 			<mapZone other="Mountain Standard Time" territory="001" type="America/Denver"/> | ||||
| 			<mapZone other="Mountain Standard Time" territory="CA" type="America/Edmonton America/Cambridge_Bay America/Inuvik America/Yellowknife"/> | ||||
| 			<mapZone other="Mountain Standard Time" territory="MX" type="America/Ojinaga"/> | ||||
| 			<mapZone other="Mountain Standard Time" territory="US" type="America/Denver America/Boise"/> | ||||
| 			<mapZone other="Mountain Standard Time" territory="ZZ" type="MST7MDT"/> | ||||
|  | ||||
| 			<!-- (UTC-06:00) Central America --> | ||||
| 			<mapZone other="Central America Standard Time" territory="001" type="America/Guatemala"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="BZ" type="America/Belize"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="CR" type="America/Costa_Rica"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="EC" type="Pacific/Galapagos"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="GT" type="America/Guatemala"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="HN" type="America/Tegucigalpa"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="NI" type="America/Managua"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="SV" type="America/El_Salvador"/> | ||||
| 			<mapZone other="Central America Standard Time" territory="ZZ" type="Etc/GMT+6"/> | ||||
|  | ||||
| 			<!-- (UTC-06:00) Central Time (US & Canada) --> | ||||
| 			<mapZone other="Central Standard Time" territory="001" type="America/Chicago"/> | ||||
| 			<mapZone other="Central Standard Time" territory="CA" type="America/Winnipeg America/Rainy_River America/Rankin_Inlet America/Resolute"/> | ||||
| 			<mapZone other="Central Standard Time" territory="MX" type="America/Matamoros"/> | ||||
| 			<mapZone other="Central Standard Time" territory="US" type="America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee America/North_Dakota/Beulah America/North_Dakota/Center America/North_Dakota/New_Salem"/> | ||||
| 			<mapZone other="Central Standard Time" territory="ZZ" type="CST6CDT"/> | ||||
|  | ||||
| 			<!-- (UTC-06:00) Easter Island --> | ||||
| 			<mapZone other="Easter Island Standard Time" territory="001" type="Pacific/Easter"/> | ||||
| 			<mapZone other="Easter Island Standard Time" territory="CL" type="Pacific/Easter"/> | ||||
|  | ||||
| 			<!-- (UTC-06:00) Guadalajara, Mexico City, Monterrey --> | ||||
| 			<mapZone other="Central Standard Time (Mexico)" territory="001" type="America/Mexico_City"/> | ||||
| 			<mapZone other="Central Standard Time (Mexico)" territory="MX" type="America/Mexico_City America/Bahia_Banderas America/Merida America/Monterrey"/> | ||||
|  | ||||
| 			<!-- (UTC-06:00) Saskatchewan --> | ||||
| 			<mapZone other="Canada Central Standard Time" territory="001" type="America/Regina"/> | ||||
| 			<mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/> | ||||
|  | ||||
| 			<!-- (UTC-05:00) Bogota, Lima, Quito, Rio Branco --> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="001" type="America/Bogota"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="BR" type="America/Rio_Branco America/Eirunepe"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="CA" type="America/Coral_Harbour"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="CO" type="America/Bogota"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="EC" type="America/Guayaquil"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="JM" type="America/Jamaica"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="KY" type="America/Cayman"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="PA" type="America/Panama"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="PE" type="America/Lima"/> | ||||
| 			<mapZone other="SA Pacific Standard Time" territory="ZZ" type="Etc/GMT+5"/> | ||||
|  | ||||
| 			<!-- (UTC-05:00) Chetumal --> | ||||
| 			<mapZone other="Eastern Standard Time (Mexico)" territory="001" type="America/Cancun"/> | ||||
| 			<mapZone other="Eastern Standard Time (Mexico)" territory="MX" type="America/Cancun"/> | ||||
|  | ||||
| 			<!-- (UTC-05:00) Eastern Time (US & Canada) --> | ||||
| 			<mapZone other="Eastern Standard Time" territory="001" type="America/New_York"/> | ||||
| 			<mapZone other="Eastern Standard Time" territory="BS" type="America/Nassau"/> | ||||
| 			<mapZone other="Eastern Standard Time" territory="CA" type="America/Toronto America/Iqaluit America/Montreal America/Nipigon America/Pangnirtung America/Thunder_Bay"/> | ||||
| 			<mapZone other="Eastern Standard Time" territory="US" type="America/New_York America/Detroit America/Indiana/Petersburg America/Indiana/Vincennes America/Indiana/Winamac America/Kentucky/Monticello America/Louisville"/> | ||||
| 			<mapZone other="Eastern Standard Time" territory="ZZ" type="EST5EDT"/> | ||||
|  | ||||
| 			<!-- (UTC-05:00) Haiti --> | ||||
| 			<mapZone other="Haiti Standard Time" territory="001" type="America/Port-au-Prince"/> | ||||
| 			<mapZone other="Haiti Standard Time" territory="HT" type="America/Port-au-Prince"/> | ||||
|  | ||||
| 			<!-- (UTC-05:00) Havana --> | ||||
| 			<mapZone other="Cuba Standard Time" territory="001" type="America/Havana"/> | ||||
| 			<mapZone other="Cuba Standard Time" territory="CU" type="America/Havana"/> | ||||
|  | ||||
| 			<!-- (UTC-05:00) Indiana (East) --> | ||||
| 			<mapZone other="US Eastern Standard Time" territory="001" type="America/Indianapolis"/> | ||||
| 			<mapZone other="US Eastern Standard Time" territory="US" type="America/Indianapolis America/Indiana/Marengo America/Indiana/Vevay"/> | ||||
|  | ||||
| 			<!-- (UTC-04:00) Asuncion --> | ||||
| 			<mapZone other="Paraguay Standard Time" territory="001" type="America/Asuncion"/> | ||||
| 			<mapZone other="Paraguay Standard Time" territory="PY" type="America/Asuncion"/> | ||||
|  | ||||
| 			<!-- (UTC-04:00) Atlantic Time (Canada) --> | ||||
| 			<mapZone other="Atlantic Standard Time" territory="001" type="America/Halifax"/> | ||||
| 			<mapZone other="Atlantic Standard Time" territory="BM" type="Atlantic/Bermuda"/> | ||||
| 			<mapZone other="Atlantic Standard Time" territory="CA" type="America/Halifax America/Glace_Bay America/Goose_Bay America/Moncton"/> | ||||
| 			<mapZone other="Atlantic Standard Time" territory="GL" type="America/Thule"/> | ||||
|  | ||||
| 			<!-- (UTC-04:00) Caracas --> | ||||
| 			<mapZone other="Venezuela Standard Time" territory="001" type="America/Caracas"/> | ||||
| 			<mapZone other="Venezuela Standard Time" territory="VE" type="America/Caracas"/> | ||||
|  | ||||
| 			<!-- (UTC-04:00) Cuiaba --> | ||||
| 			<mapZone other="Central Brazilian Standard Time" territory="001" type="America/Cuiaba"/> | ||||
| 			<mapZone other="Central Brazilian Standard Time" territory="BR" type="America/Cuiaba America/Campo_Grande"/> | ||||
|  | ||||
| 			<!-- (UTC-04:00) Georgetown, La Paz, Manaus, San Juan --> | ||||
| 			<mapZone other="SA Western Standard Time" territory="001" type="America/La_Paz"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="AG" type="America/Antigua"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="AI" type="America/Anguilla"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="AW" type="America/Aruba"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="BB" type="America/Barbados"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="BL" type="America/St_Barthelemy"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="BO" type="America/La_Paz"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="BQ" type="America/Kralendijk"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="BR" type="America/Manaus America/Boa_Vista America/Porto_Velho"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="CA" type="America/Blanc-Sablon"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="CW" type="America/Curacao"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="DM" type="America/Dominica"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="DO" type="America/Santo_Domingo"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="GD" type="America/Grenada"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="GP" type="America/Guadeloupe"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="GY" type="America/Guyana"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="KN" type="America/St_Kitts"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="LC" type="America/St_Lucia"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="MF" type="America/Marigot"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="MQ" type="America/Martinique"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="MS" type="America/Montserrat"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="PR" type="America/Puerto_Rico"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="SX" type="America/Lower_Princes"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="TT" type="America/Port_of_Spain"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="VC" type="America/St_Vincent"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="VG" type="America/Tortola"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="VI" type="America/St_Thomas"/> | ||||
| 			<mapZone other="SA Western Standard Time" territory="ZZ" type="Etc/GMT+4"/> | ||||
|  | ||||
| 			<!-- (UTC-04:00) Santiago --> | ||||
| 			<mapZone other="Pacific SA Standard Time" territory="001" type="America/Santiago"/> | ||||
| 			<mapZone other="Pacific SA Standard Time" territory="AQ" type="Antarctica/Palmer"/> | ||||
| 			<mapZone other="Pacific SA Standard Time" territory="CL" type="America/Santiago"/> | ||||
|  | ||||
| 			<!-- (UTC-04:00) Turks and Caicos --> | ||||
| 			<mapZone other="Turks And Caicos Standard Time" territory="001" type="America/Grand_Turk"/> | ||||
| 			<mapZone other="Turks And Caicos Standard Time" territory="TC" type="America/Grand_Turk"/> | ||||
|  | ||||
| 			<!-- (UTC-03:30) Newfoundland --> | ||||
| 			<mapZone other="Newfoundland Standard Time" territory="001" type="America/St_Johns"/> | ||||
| 			<mapZone other="Newfoundland Standard Time" territory="CA" type="America/St_Johns"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) Araguaina --> | ||||
| 			<mapZone other="Tocantins Standard Time" territory="001" type="America/Araguaina"/> | ||||
| 			<mapZone other="Tocantins Standard Time" territory="BR" type="America/Araguaina"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) Brasilia --> | ||||
| 			<mapZone other="E. South America Standard Time" territory="001" type="America/Sao_Paulo"/> | ||||
| 			<mapZone other="E. South America Standard Time" territory="BR" type="America/Sao_Paulo"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) Cayenne, Fortaleza --> | ||||
| 			<mapZone other="SA Eastern Standard Time" territory="001" type="America/Cayenne"/> | ||||
| 			<mapZone other="SA Eastern Standard Time" territory="AQ" type="Antarctica/Rothera"/> | ||||
| 			<mapZone other="SA Eastern Standard Time" territory="BR" type="America/Fortaleza America/Belem America/Maceio America/Recife America/Santarem"/> | ||||
| 			<mapZone other="SA Eastern Standard Time" territory="FK" type="Atlantic/Stanley"/> | ||||
| 			<mapZone other="SA Eastern Standard Time" territory="GF" type="America/Cayenne"/> | ||||
| 			<mapZone other="SA Eastern Standard Time" territory="SR" type="America/Paramaribo"/> | ||||
| 			<mapZone other="SA Eastern Standard Time" territory="ZZ" type="Etc/GMT+3"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) City of Buenos Aires --> | ||||
| 			<mapZone other="Argentina Standard Time" territory="001" type="America/Buenos_Aires"/> | ||||
| 			<mapZone other="Argentina Standard Time" territory="AR" type="America/Buenos_Aires America/Argentina/La_Rioja America/Argentina/Rio_Gallegos America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis America/Argentina/Tucuman America/Argentina/Ushuaia America/Catamarca America/Cordoba America/Jujuy America/Mendoza"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) Greenland --> | ||||
| 			<mapZone other="Greenland Standard Time" territory="001" type="America/Godthab"/> | ||||
| 			<mapZone other="Greenland Standard Time" territory="GL" type="America/Godthab"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) Montevideo --> | ||||
| 			<mapZone other="Montevideo Standard Time" territory="001" type="America/Montevideo"/> | ||||
| 			<mapZone other="Montevideo Standard Time" territory="UY" type="America/Montevideo"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) Saint Pierre and Miquelon --> | ||||
| 			<mapZone other="Saint Pierre Standard Time" territory="001" type="America/Miquelon"/> | ||||
| 			<mapZone other="Saint Pierre Standard Time" territory="PM" type="America/Miquelon"/> | ||||
|  | ||||
| 			<!-- (UTC-03:00) Salvador --> | ||||
| 			<mapZone other="Bahia Standard Time" territory="001" type="America/Bahia"/> | ||||
| 			<mapZone other="Bahia Standard Time" territory="BR" type="America/Bahia"/> | ||||
|  | ||||
| 			<!-- (UTC-02:00) Coordinated Universal Time-02 --> | ||||
| 			<mapZone other="UTC-02" territory="001" type="Etc/GMT+2"/> | ||||
| 			<mapZone other="UTC-02" territory="BR" type="America/Noronha"/> | ||||
| 			<mapZone other="UTC-02" territory="GS" type="Atlantic/South_Georgia"/> | ||||
| 			<mapZone other="UTC-02" territory="ZZ" type="Etc/GMT+2"/> | ||||
|  | ||||
| 			<!-- (UTC-01:00) Azores --> | ||||
| 			<mapZone other="Azores Standard Time" territory="001" type="Atlantic/Azores"/> | ||||
| 			<mapZone other="Azores Standard Time" territory="GL" type="America/Scoresbysund"/> | ||||
| 			<mapZone other="Azores Standard Time" territory="PT" type="Atlantic/Azores"/> | ||||
|  | ||||
| 			<!-- (UTC-01:00) Cabo Verde Is. --> | ||||
| 			<mapZone other="Cape Verde Standard Time" territory="001" type="Atlantic/Cape_Verde"/> | ||||
| 			<mapZone other="Cape Verde Standard Time" territory="CV" type="Atlantic/Cape_Verde"/> | ||||
| 			<mapZone other="Cape Verde Standard Time" territory="ZZ" type="Etc/GMT+1"/> | ||||
|  | ||||
| 			<!-- (UTC) Coordinated Universal Time --> | ||||
| 			<mapZone other="UTC" territory="001" type="Etc/GMT"/> | ||||
| 			<mapZone other="UTC" territory="GL" type="America/Danmarkshavn"/> | ||||
| 			<mapZone other="UTC" territory="ZZ" type="Etc/GMT"/> | ||||
|  | ||||
| 			<!-- (UTC+00:00) Casablanca --> | ||||
| 			<mapZone other="Morocco Standard Time" territory="001" type="Africa/Casablanca"/> | ||||
| 			<mapZone other="Morocco Standard Time" territory="EH" type="Africa/El_Aaiun"/> | ||||
| 			<mapZone other="Morocco Standard Time" territory="MA" type="Africa/Casablanca"/> | ||||
|  | ||||
| 			<!-- (UTC+00:00) Dublin, Edinburgh, Lisbon, London --> | ||||
| 			<mapZone other="GMT Standard Time" territory="001" type="Europe/London"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="ES" type="Atlantic/Canary"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="FO" type="Atlantic/Faeroe"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="GB" type="Europe/London"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="GG" type="Europe/Guernsey"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="IE" type="Europe/Dublin"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="IM" type="Europe/Isle_of_Man"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="JE" type="Europe/Jersey"/> | ||||
| 			<mapZone other="GMT Standard Time" territory="PT" type="Europe/Lisbon Atlantic/Madeira"/> | ||||
|  | ||||
| 			<!-- (UTC+00:00) Monrovia, Reykjavik --> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="001" type="Atlantic/Reykjavik"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="BF" type="Africa/Ouagadougou"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="CI" type="Africa/Abidjan"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="GH" type="Africa/Accra"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="GM" type="Africa/Banjul"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="GN" type="Africa/Conakry"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="GW" type="Africa/Bissau"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="IS" type="Atlantic/Reykjavik"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="LR" type="Africa/Monrovia"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="ML" type="Africa/Bamako"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="MR" type="Africa/Nouakchott"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="SH" type="Atlantic/St_Helena"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="SL" type="Africa/Freetown"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="SN" type="Africa/Dakar"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="ST" type="Africa/Sao_Tome"/> | ||||
| 			<mapZone other="Greenwich Standard Time" territory="TG" type="Africa/Lome"/> | ||||
|  | ||||
| 			<!-- (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna --> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="001" type="Europe/Berlin"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="AD" type="Europe/Andorra"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="AT" type="Europe/Vienna"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="CH" type="Europe/Zurich"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="DE" type="Europe/Berlin Europe/Busingen"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="GI" type="Europe/Gibraltar"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="IT" type="Europe/Rome"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="LI" type="Europe/Vaduz"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="LU" type="Europe/Luxembourg"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="MC" type="Europe/Monaco"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="MT" type="Europe/Malta"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="NL" type="Europe/Amsterdam"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="NO" type="Europe/Oslo"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="SE" type="Europe/Stockholm"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="SJ" type="Arctic/Longyearbyen"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="SM" type="Europe/San_Marino"/> | ||||
| 			<mapZone other="W. Europe Standard Time" territory="VA" type="Europe/Vatican"/> | ||||
|  | ||||
| 			<!-- (UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague --> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="001" type="Europe/Budapest"/> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="AL" type="Europe/Tirane"/> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="CZ" type="Europe/Prague"/> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="HU" type="Europe/Budapest"/> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="ME" type="Europe/Podgorica"/> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="RS" type="Europe/Belgrade"/> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="SI" type="Europe/Ljubljana"/> | ||||
| 			<mapZone other="Central Europe Standard Time" territory="SK" type="Europe/Bratislava"/> | ||||
|  | ||||
| 			<!-- (UTC+01:00) Brussels, Copenhagen, Madrid, Paris --> | ||||
| 			<mapZone other="Romance Standard Time" territory="001" type="Europe/Paris"/> | ||||
| 			<mapZone other="Romance Standard Time" territory="BE" type="Europe/Brussels"/> | ||||
| 			<mapZone other="Romance Standard Time" territory="DK" type="Europe/Copenhagen"/> | ||||
| 			<mapZone other="Romance Standard Time" territory="ES" type="Europe/Madrid Africa/Ceuta"/> | ||||
| 			<mapZone other="Romance Standard Time" territory="FR" type="Europe/Paris"/> | ||||
|  | ||||
| 			<!-- (UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb --> | ||||
| 			<mapZone other="Central European Standard Time" territory="001" type="Europe/Warsaw"/> | ||||
| 			<mapZone other="Central European Standard Time" territory="BA" type="Europe/Sarajevo"/> | ||||
| 			<mapZone other="Central European Standard Time" territory="HR" type="Europe/Zagreb"/> | ||||
| 			<mapZone other="Central European Standard Time" territory="MK" type="Europe/Skopje"/> | ||||
| 			<mapZone other="Central European Standard Time" territory="PL" type="Europe/Warsaw"/> | ||||
|  | ||||
| 			<!-- (UTC+01:00) West Central Africa --> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="001" type="Africa/Lagos"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="AO" type="Africa/Luanda"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="BJ" type="Africa/Porto-Novo"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="CD" type="Africa/Kinshasa"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="CF" type="Africa/Bangui"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="CG" type="Africa/Brazzaville"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="CM" type="Africa/Douala"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="DZ" type="Africa/Algiers"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="GA" type="Africa/Libreville"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="GQ" type="Africa/Malabo"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="NE" type="Africa/Niamey"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="NG" type="Africa/Lagos"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="TD" type="Africa/Ndjamena"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="TN" type="Africa/Tunis"/> | ||||
| 			<mapZone other="W. Central Africa Standard Time" territory="ZZ" type="Etc/GMT-1"/> | ||||
|  | ||||
| 			<!-- (UTC+01:00) Windhoek --> | ||||
| 			<mapZone other="Namibia Standard Time" territory="001" type="Africa/Windhoek"/> | ||||
| 			<mapZone other="Namibia Standard Time" territory="NA" type="Africa/Windhoek"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Amman --> | ||||
| 			<mapZone other="Jordan Standard Time" territory="001" type="Asia/Amman"/> | ||||
| 			<mapZone other="Jordan Standard Time" territory="JO" type="Asia/Amman"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Athens, Bucharest --> | ||||
| 			<mapZone other="GTB Standard Time" territory="001" type="Europe/Bucharest"/> | ||||
| 			<mapZone other="GTB Standard Time" territory="CY" type="Asia/Nicosia"/> | ||||
| 			<mapZone other="GTB Standard Time" territory="GR" type="Europe/Athens"/> | ||||
| 			<mapZone other="GTB Standard Time" territory="RO" type="Europe/Bucharest"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Beirut --> | ||||
| 			<mapZone other="Middle East Standard Time" territory="001" type="Asia/Beirut"/> | ||||
| 			<mapZone other="Middle East Standard Time" territory="LB" type="Asia/Beirut"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Cairo --> | ||||
| 			<mapZone other="Egypt Standard Time" territory="001" type="Africa/Cairo"/> | ||||
| 			<mapZone other="Egypt Standard Time" territory="EG" type="Africa/Cairo"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Chisinau --> | ||||
| 			<mapZone other="E. Europe Standard Time" territory="001" type="Europe/Chisinau"/> | ||||
| 			<mapZone other="E. Europe Standard Time" territory="MD" type="Europe/Chisinau"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Damascus --> | ||||
| 			<mapZone other="Syria Standard Time" territory="001" type="Asia/Damascus"/> | ||||
| 			<mapZone other="Syria Standard Time" territory="SY" type="Asia/Damascus"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Gaza, Hebron --> | ||||
| 			<mapZone other="West Bank Standard Time" territory="001" type="Asia/Hebron"/> | ||||
| 			<mapZone other="West Bank Standard Time" territory="PS" type="Asia/Hebron Asia/Gaza"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Harare, Pretoria --> | ||||
| 			<mapZone other="South Africa Standard Time" territory="001" type="Africa/Johannesburg"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="BI" type="Africa/Bujumbura"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="BW" type="Africa/Gaborone"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="CD" type="Africa/Lubumbashi"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="LS" type="Africa/Maseru"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="MW" type="Africa/Blantyre"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="MZ" type="Africa/Maputo"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="RW" type="Africa/Kigali"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="SZ" type="Africa/Mbabane"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="ZA" type="Africa/Johannesburg"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="ZM" type="Africa/Lusaka"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="ZW" type="Africa/Harare"/> | ||||
| 			<mapZone other="South Africa Standard Time" territory="ZZ" type="Etc/GMT-2"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius --> | ||||
| 			<mapZone other="FLE Standard Time" territory="001" type="Europe/Kiev"/> | ||||
| 			<mapZone other="FLE Standard Time" territory="AX" type="Europe/Mariehamn"/> | ||||
| 			<mapZone other="FLE Standard Time" territory="BG" type="Europe/Sofia"/> | ||||
| 			<mapZone other="FLE Standard Time" territory="EE" type="Europe/Tallinn"/> | ||||
| 			<mapZone other="FLE Standard Time" territory="FI" type="Europe/Helsinki"/> | ||||
| 			<mapZone other="FLE Standard Time" territory="LT" type="Europe/Vilnius"/> | ||||
| 			<mapZone other="FLE Standard Time" territory="LV" type="Europe/Riga"/> | ||||
| 			<mapZone other="FLE Standard Time" territory="UA" type="Europe/Kiev Europe/Uzhgorod Europe/Zaporozhye"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Istanbul --> | ||||
| 			<mapZone other="Turkey Standard Time" territory="001" type="Europe/Istanbul"/> | ||||
| 			<mapZone other="Turkey Standard Time" territory="TR" type="Europe/Istanbul"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Jerusalem --> | ||||
| 			<mapZone other="Israel Standard Time" territory="001" type="Asia/Jerusalem"/> | ||||
| 			<mapZone other="Israel Standard Time" territory="IL" type="Asia/Jerusalem"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Kaliningrad --> | ||||
| 			<mapZone other="Kaliningrad Standard Time" territory="001" type="Europe/Kaliningrad"/> | ||||
| 			<mapZone other="Kaliningrad Standard Time" territory="RU" type="Europe/Kaliningrad"/> | ||||
|  | ||||
| 			<!-- (UTC+02:00) Tripoli --> | ||||
| 			<mapZone other="Libya Standard Time" territory="001" type="Africa/Tripoli"/> | ||||
| 			<mapZone other="Libya Standard Time" territory="LY" type="Africa/Tripoli"/> | ||||
|  | ||||
| 			<!-- (UTC+03:00) Baghdad --> | ||||
| 			<mapZone other="Arabic Standard Time" territory="001" type="Asia/Baghdad"/> | ||||
| 			<mapZone other="Arabic Standard Time" territory="IQ" type="Asia/Baghdad"/> | ||||
|  | ||||
| 			<!-- (UTC+03:00) Kuwait, Riyadh --> | ||||
| 			<mapZone other="Arab Standard Time" territory="001" type="Asia/Riyadh"/> | ||||
| 			<mapZone other="Arab Standard Time" territory="BH" type="Asia/Bahrain"/> | ||||
| 			<mapZone other="Arab Standard Time" territory="KW" type="Asia/Kuwait"/> | ||||
| 			<mapZone other="Arab Standard Time" territory="QA" type="Asia/Qatar"/> | ||||
| 			<mapZone other="Arab Standard Time" territory="SA" type="Asia/Riyadh"/> | ||||
| 			<mapZone other="Arab Standard Time" territory="YE" type="Asia/Aden"/> | ||||
|  | ||||
| 			<!-- (UTC+03:00) Minsk --> | ||||
| 			<mapZone other="Belarus Standard Time" territory="001" type="Europe/Minsk"/> | ||||
| 			<mapZone other="Belarus Standard Time" territory="BY" type="Europe/Minsk"/> | ||||
|  | ||||
| 			<!-- (UTC+03:00) Moscow, St. Petersburg, Volgograd --> | ||||
| 			<mapZone other="Russian Standard Time" territory="001" type="Europe/Moscow"/> | ||||
| 			<mapZone other="Russian Standard Time" territory="RU" type="Europe/Moscow Europe/Kirov Europe/Volgograd"/> | ||||
| 			<mapZone other="Russian Standard Time" territory="UA" type="Europe/Simferopol"/> | ||||
|  | ||||
| 			<!-- (UTC+03:00) Nairobi --> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="001" type="Africa/Nairobi"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="AQ" type="Antarctica/Syowa"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="DJ" type="Africa/Djibouti"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="ER" type="Africa/Asmera"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="ET" type="Africa/Addis_Ababa"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="KE" type="Africa/Nairobi"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="KM" type="Indian/Comoro"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="MG" type="Indian/Antananarivo"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="SD" type="Africa/Khartoum"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="SO" type="Africa/Mogadishu"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="SS" type="Africa/Juba"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="TZ" type="Africa/Dar_es_Salaam"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="UG" type="Africa/Kampala"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="YT" type="Indian/Mayotte"/> | ||||
| 			<mapZone other="E. Africa Standard Time" territory="ZZ" type="Etc/GMT-3"/> | ||||
|  | ||||
| 			<!-- (UTC+03:30) Tehran --> | ||||
| 			<mapZone other="Iran Standard Time" territory="001" type="Asia/Tehran"/> | ||||
| 			<mapZone other="Iran Standard Time" territory="IR" type="Asia/Tehran"/> | ||||
|  | ||||
| 			<!-- (UTC+04:00) Abu Dhabi, Muscat --> | ||||
| 			<mapZone other="Arabian Standard Time" territory="001" type="Asia/Dubai"/> | ||||
| 			<mapZone other="Arabian Standard Time" territory="AE" type="Asia/Dubai"/> | ||||
| 			<mapZone other="Arabian Standard Time" territory="OM" type="Asia/Muscat"/> | ||||
| 			<mapZone other="Arabian Standard Time" territory="ZZ" type="Etc/GMT-4"/> | ||||
|  | ||||
| 			<!-- (UTC+04:00) Astrakhan, Ulyanovsk --> | ||||
| 			<mapZone other="Astrakhan Standard Time" territory="001" type="Europe/Astrakhan"/> | ||||
| 			<mapZone other="Astrakhan Standard Time" territory="RU" type="Europe/Astrakhan Europe/Ulyanovsk"/> | ||||
|  | ||||
| 			<!-- (UTC+04:00) Baku --> | ||||
| 			<mapZone other="Azerbaijan Standard Time" territory="001" type="Asia/Baku"/> | ||||
| 			<mapZone other="Azerbaijan Standard Time" territory="AZ" type="Asia/Baku"/> | ||||
|  | ||||
| 			<!-- (UTC+04:00) Izhevsk, Samara --> | ||||
| 			<mapZone other="Russia Time Zone 3" territory="001" type="Europe/Samara"/> | ||||
| 			<mapZone other="Russia Time Zone 3" territory="RU" type="Europe/Samara"/> | ||||
|  | ||||
| 			<!-- (UTC+04:00) Port Louis --> | ||||
| 			<mapZone other="Mauritius Standard Time" territory="001" type="Indian/Mauritius"/> | ||||
| 			<mapZone other="Mauritius Standard Time" territory="MU" type="Indian/Mauritius"/> | ||||
| 			<mapZone other="Mauritius Standard Time" territory="RE" type="Indian/Reunion"/> | ||||
| 			<mapZone other="Mauritius Standard Time" territory="SC" type="Indian/Mahe"/> | ||||
|  | ||||
| 			<!-- (UTC+04:00) Tbilisi --> | ||||
| 			<mapZone other="Georgian Standard Time" territory="001" type="Asia/Tbilisi"/> | ||||
| 			<mapZone other="Georgian Standard Time" territory="GE" type="Asia/Tbilisi"/> | ||||
|  | ||||
| 			<!-- (UTC+04:00) Yerevan --> | ||||
| 			<mapZone other="Caucasus Standard Time" territory="001" type="Asia/Yerevan"/> | ||||
| 			<mapZone other="Caucasus Standard Time" territory="AM" type="Asia/Yerevan"/> | ||||
|  | ||||
| 			<!-- (UTC+04:30) Kabul --> | ||||
| 			<mapZone other="Afghanistan Standard Time" territory="001" type="Asia/Kabul"/> | ||||
| 			<mapZone other="Afghanistan Standard Time" territory="AF" type="Asia/Kabul"/> | ||||
|  | ||||
| 			<!-- (UTC+05:00) Ashgabat, Tashkent --> | ||||
| 			<mapZone other="West Asia Standard Time" territory="001" type="Asia/Tashkent"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="AQ" type="Antarctica/Mawson"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="KZ" type="Asia/Oral Asia/Aqtau Asia/Aqtobe"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="MV" type="Indian/Maldives"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="TF" type="Indian/Kerguelen"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="TJ" type="Asia/Dushanbe"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="TM" type="Asia/Ashgabat"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="UZ" type="Asia/Tashkent Asia/Samarkand"/> | ||||
| 			<mapZone other="West Asia Standard Time" territory="ZZ" type="Etc/GMT-5"/> | ||||
|  | ||||
| 			<!-- (UTC+05:00) Ekaterinburg --> | ||||
| 			<mapZone other="Ekaterinburg Standard Time" territory="001" type="Asia/Yekaterinburg"/> | ||||
| 			<mapZone other="Ekaterinburg Standard Time" territory="RU" type="Asia/Yekaterinburg"/> | ||||
|  | ||||
| 			<!-- (UTC+05:00) Islamabad, Karachi --> | ||||
| 			<mapZone other="Pakistan Standard Time" territory="001" type="Asia/Karachi"/> | ||||
| 			<mapZone other="Pakistan Standard Time" territory="PK" type="Asia/Karachi"/> | ||||
|  | ||||
| 			<!-- (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi --> | ||||
| 			<mapZone other="India Standard Time" territory="001" type="Asia/Calcutta"/> | ||||
| 			<mapZone other="India Standard Time" territory="IN" type="Asia/Calcutta"/> | ||||
|  | ||||
| 			<!-- (UTC+05:30) Sri Jayawardenepura --> | ||||
| 			<mapZone other="Sri Lanka Standard Time" territory="001" type="Asia/Colombo"/> | ||||
| 			<mapZone other="Sri Lanka Standard Time" territory="LK" type="Asia/Colombo"/> | ||||
|  | ||||
| 			<!-- (UTC+05:45) Kathmandu --> | ||||
| 			<mapZone other="Nepal Standard Time" territory="001" type="Asia/Katmandu"/> | ||||
| 			<mapZone other="Nepal Standard Time" territory="NP" type="Asia/Katmandu"/> | ||||
|  | ||||
| 			<!-- (UTC+06:00) Astana --> | ||||
| 			<mapZone other="Central Asia Standard Time" territory="001" type="Asia/Almaty"/> | ||||
| 			<mapZone other="Central Asia Standard Time" territory="AQ" type="Antarctica/Vostok"/> | ||||
| 			<mapZone other="Central Asia Standard Time" territory="CN" type="Asia/Urumqi"/> | ||||
| 			<mapZone other="Central Asia Standard Time" territory="IO" type="Indian/Chagos"/> | ||||
| 			<mapZone other="Central Asia Standard Time" territory="KG" type="Asia/Bishkek"/> | ||||
| 			<mapZone other="Central Asia Standard Time" territory="KZ" type="Asia/Almaty Asia/Qyzylorda"/> | ||||
| 			<mapZone other="Central Asia Standard Time" territory="ZZ" type="Etc/GMT-6"/> | ||||
|  | ||||
| 			<!-- (UTC+06:00) Dhaka --> | ||||
| 			<mapZone other="Bangladesh Standard Time" territory="001" type="Asia/Dhaka"/> | ||||
| 			<mapZone other="Bangladesh Standard Time" territory="BD" type="Asia/Dhaka"/> | ||||
| 			<mapZone other="Bangladesh Standard Time" territory="BT" type="Asia/Thimphu"/> | ||||
|  | ||||
| 			<!-- (UTC+06:00) Omsk --> | ||||
| 			<mapZone other="Omsk Standard Time" territory="001" type="Asia/Omsk"/> | ||||
| 			<mapZone other="Omsk Standard Time" territory="RU" type="Asia/Omsk"/> | ||||
|  | ||||
| 			<!-- (UTC+06:30) Yangon (Rangoon) --> | ||||
| 			<mapZone other="Myanmar Standard Time" territory="001" type="Asia/Rangoon"/> | ||||
| 			<mapZone other="Myanmar Standard Time" territory="CC" type="Indian/Cocos"/> | ||||
| 			<mapZone other="Myanmar Standard Time" territory="MM" type="Asia/Rangoon"/> | ||||
|  | ||||
| 			<!-- (UTC+07:00) Bangkok, Hanoi, Jakarta --> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="001" type="Asia/Bangkok"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="AQ" type="Antarctica/Davis"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="CX" type="Indian/Christmas"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="ID" type="Asia/Jakarta Asia/Pontianak"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="KH" type="Asia/Phnom_Penh"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="LA" type="Asia/Vientiane"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="TH" type="Asia/Bangkok"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="VN" type="Asia/Saigon"/> | ||||
| 			<mapZone other="SE Asia Standard Time" territory="ZZ" type="Etc/GMT-7"/> | ||||
|  | ||||
| 			<!-- (UTC+07:00) Barnaul, Gorno-Altaysk --> | ||||
| 			<mapZone other="Altai Standard Time" territory="001" type="Asia/Barnaul"/> | ||||
| 			<mapZone other="Altai Standard Time" territory="RU" type="Asia/Barnaul"/> | ||||
|  | ||||
| 			<!-- (UTC+07:00) Hovd --> | ||||
| 			<mapZone other="W. Mongolia Standard Time" territory="001" type="Asia/Hovd"/> | ||||
| 			<mapZone other="W. Mongolia Standard Time" territory="MN" type="Asia/Hovd"/> | ||||
|  | ||||
| 			<!-- (UTC+07:00) Krasnoyarsk --> | ||||
| 			<mapZone other="North Asia Standard Time" territory="001" type="Asia/Krasnoyarsk"/> | ||||
| 			<mapZone other="North Asia Standard Time" territory="RU" type="Asia/Krasnoyarsk Asia/Novokuznetsk"/> | ||||
|  | ||||
| 			<!-- (UTC+07:00) Novosibirsk --> | ||||
| 			<mapZone other="N. Central Asia Standard Time" territory="001" type="Asia/Novosibirsk"/> | ||||
| 			<mapZone other="N. Central Asia Standard Time" territory="RU" type="Asia/Novosibirsk"/> | ||||
|  | ||||
| 			<!-- (UTC+07:00) Tomsk --> | ||||
| 			<mapZone other="Tomsk Standard Time" territory="001" type="Asia/Tomsk"/> | ||||
| 			<mapZone other="Tomsk Standard Time" territory="RU" type="Asia/Tomsk"/> | ||||
|  | ||||
| 			<!-- (UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi --> | ||||
| 			<mapZone other="China Standard Time" territory="001" type="Asia/Shanghai"/> | ||||
| 			<mapZone other="China Standard Time" territory="CN" type="Asia/Shanghai"/> | ||||
| 			<mapZone other="China Standard Time" territory="HK" type="Asia/Hong_Kong"/> | ||||
| 			<mapZone other="China Standard Time" territory="MO" type="Asia/Macau"/> | ||||
|  | ||||
| 			<!-- (UTC+08:00) Irkutsk --> | ||||
| 			<mapZone other="North Asia East Standard Time" territory="001" type="Asia/Irkutsk"/> | ||||
| 			<mapZone other="North Asia East Standard Time" territory="RU" type="Asia/Irkutsk"/> | ||||
|  | ||||
| 			<!-- (UTC+08:00) Kuala Lumpur, Singapore --> | ||||
| 			<mapZone other="Singapore Standard Time" territory="001" type="Asia/Singapore"/> | ||||
| 			<mapZone other="Singapore Standard Time" territory="BN" type="Asia/Brunei"/> | ||||
| 			<mapZone other="Singapore Standard Time" territory="ID" type="Asia/Makassar"/> | ||||
| 			<mapZone other="Singapore Standard Time" territory="MY" type="Asia/Kuala_Lumpur Asia/Kuching"/> | ||||
| 			<mapZone other="Singapore Standard Time" territory="PH" type="Asia/Manila"/> | ||||
| 			<mapZone other="Singapore Standard Time" territory="SG" type="Asia/Singapore"/> | ||||
| 			<mapZone other="Singapore Standard Time" territory="ZZ" type="Etc/GMT-8"/> | ||||
|  | ||||
| 			<!-- (UTC+08:00) Perth --> | ||||
| 			<mapZone other="W. Australia Standard Time" territory="001" type="Australia/Perth"/> | ||||
| 			<mapZone other="W. Australia Standard Time" territory="AU" type="Australia/Perth"/> | ||||
|  | ||||
| 			<!-- (UTC+08:00) Taipei --> | ||||
| 			<mapZone other="Taipei Standard Time" territory="001" type="Asia/Taipei"/> | ||||
| 			<mapZone other="Taipei Standard Time" territory="TW" type="Asia/Taipei"/> | ||||
|  | ||||
| 			<!-- (UTC+08:00) Ulaanbaatar --> | ||||
| 			<mapZone other="Ulaanbaatar Standard Time" territory="001" type="Asia/Ulaanbaatar"/> | ||||
| 			<mapZone other="Ulaanbaatar Standard Time" territory="MN" type="Asia/Ulaanbaatar Asia/Choibalsan"/> | ||||
|  | ||||
| 			<!-- (UTC+08:30) Pyongyang --> | ||||
| 			<mapZone other="North Korea Standard Time" territory="001" type="Asia/Pyongyang"/> | ||||
| 			<mapZone other="North Korea Standard Time" territory="KP" type="Asia/Pyongyang"/> | ||||
|  | ||||
| 			<!-- (UTC+08:45) Eucla --> | ||||
| 			<mapZone other="Aus Central W. Standard Time" territory="001" type="Australia/Eucla"/> | ||||
| 			<mapZone other="Aus Central W. Standard Time" territory="AU" type="Australia/Eucla"/> | ||||
|  | ||||
| 			<!-- (UTC+09:00) Chita --> | ||||
| 			<mapZone other="Transbaikal Standard Time" territory="001" type="Asia/Chita"/> | ||||
| 			<mapZone other="Transbaikal Standard Time" territory="RU" type="Asia/Chita"/> | ||||
|  | ||||
| 			<!-- (UTC+09:00) Osaka, Sapporo, Tokyo --> | ||||
| 			<mapZone other="Tokyo Standard Time" territory="001" type="Asia/Tokyo"/> | ||||
| 			<mapZone other="Tokyo Standard Time" territory="ID" type="Asia/Jayapura"/> | ||||
| 			<mapZone other="Tokyo Standard Time" territory="JP" type="Asia/Tokyo"/> | ||||
| 			<mapZone other="Tokyo Standard Time" territory="PW" type="Pacific/Palau"/> | ||||
| 			<mapZone other="Tokyo Standard Time" territory="TL" type="Asia/Dili"/> | ||||
| 			<mapZone other="Tokyo Standard Time" territory="ZZ" type="Etc/GMT-9"/> | ||||
|  | ||||
| 			<!-- (UTC+09:00) Seoul --> | ||||
| 			<mapZone other="Korea Standard Time" territory="001" type="Asia/Seoul"/> | ||||
| 			<mapZone other="Korea Standard Time" territory="KR" type="Asia/Seoul"/> | ||||
|  | ||||
| 			<!-- (UTC+09:00) Yakutsk --> | ||||
| 			<mapZone other="Yakutsk Standard Time" territory="001" type="Asia/Yakutsk"/> | ||||
| 			<mapZone other="Yakutsk Standard Time" territory="RU" type="Asia/Yakutsk Asia/Khandyga"/> | ||||
|  | ||||
| 			<!-- (UTC+09:30) Adelaide --> | ||||
| 			<mapZone other="Cen. Australia Standard Time" territory="001" type="Australia/Adelaide"/> | ||||
| 			<mapZone other="Cen. Australia Standard Time" territory="AU" type="Australia/Adelaide Australia/Broken_Hill"/> | ||||
|  | ||||
| 			<!-- (UTC+09:30) Darwin --> | ||||
| 			<mapZone other="AUS Central Standard Time" territory="001" type="Australia/Darwin"/> | ||||
| 			<mapZone other="AUS Central Standard Time" territory="AU" type="Australia/Darwin"/> | ||||
|  | ||||
| 			<!-- (UTC+10:00) Brisbane --> | ||||
| 			<mapZone other="E. Australia Standard Time" territory="001" type="Australia/Brisbane"/> | ||||
| 			<mapZone other="E. Australia Standard Time" territory="AU" type="Australia/Brisbane Australia/Lindeman"/> | ||||
|  | ||||
| 			<!-- (UTC+10:00) Canberra, Melbourne, Sydney --> | ||||
| 			<mapZone other="AUS Eastern Standard Time" territory="001" type="Australia/Sydney"/> | ||||
| 			<mapZone other="AUS Eastern Standard Time" territory="AU" type="Australia/Sydney Australia/Melbourne"/> | ||||
|  | ||||
| 			<!-- (UTC+10:00) Guam, Port Moresby --> | ||||
| 			<mapZone other="West Pacific Standard Time" territory="001" type="Pacific/Port_Moresby"/> | ||||
| 			<mapZone other="West Pacific Standard Time" territory="AQ" type="Antarctica/DumontDUrville"/> | ||||
| 			<mapZone other="West Pacific Standard Time" territory="FM" type="Pacific/Truk"/> | ||||
| 			<mapZone other="West Pacific Standard Time" territory="GU" type="Pacific/Guam"/> | ||||
| 			<mapZone other="West Pacific Standard Time" territory="MP" type="Pacific/Saipan"/> | ||||
| 			<mapZone other="West Pacific Standard Time" territory="PG" type="Pacific/Port_Moresby"/> | ||||
| 			<mapZone other="West Pacific Standard Time" territory="ZZ" type="Etc/GMT-10"/> | ||||
|  | ||||
| 			<!-- (UTC+10:00) Hobart --> | ||||
| 			<mapZone other="Tasmania Standard Time" territory="001" type="Australia/Hobart"/> | ||||
| 			<mapZone other="Tasmania Standard Time" territory="AU" type="Australia/Hobart Australia/Currie"/> | ||||
|  | ||||
| 			<!-- (UTC+10:00) Vladivostok --> | ||||
| 			<mapZone other="Vladivostok Standard Time" territory="001" type="Asia/Vladivostok"/> | ||||
| 			<mapZone other="Vladivostok Standard Time" territory="RU" type="Asia/Vladivostok Asia/Ust-Nera"/> | ||||
|  | ||||
| 			<!-- (UTC+10:30) Lord Howe Island --> | ||||
| 			<mapZone other="Lord Howe Standard Time" territory="001" type="Australia/Lord_Howe"/> | ||||
| 			<mapZone other="Lord Howe Standard Time" territory="AU" type="Australia/Lord_Howe"/> | ||||
|  | ||||
| 			<!-- (UTC+11:00) Bougainville Island --> | ||||
| 			<mapZone other="Bougainville Standard Time" territory="001" type="Pacific/Bougainville"/> | ||||
| 			<mapZone other="Bougainville Standard Time" territory="PG" type="Pacific/Bougainville"/> | ||||
|  | ||||
| 			<!-- (UTC+11:00) Chokurdakh --> | ||||
| 			<mapZone other="Russia Time Zone 10" territory="001" type="Asia/Srednekolymsk"/> | ||||
| 			<mapZone other="Russia Time Zone 10" territory="RU" type="Asia/Srednekolymsk"/> | ||||
|  | ||||
| 			<!-- (UTC+11:00) Magadan --> | ||||
| 			<mapZone other="Magadan Standard Time" territory="001" type="Asia/Magadan"/> | ||||
| 			<mapZone other="Magadan Standard Time" territory="RU" type="Asia/Magadan"/> | ||||
|  | ||||
| 			<!-- (UTC+11:00) Norfolk Island --> | ||||
| 			<mapZone other="Norfolk Standard Time" territory="001" type="Pacific/Norfolk"/> | ||||
| 			<mapZone other="Norfolk Standard Time" territory="NF" type="Pacific/Norfolk"/> | ||||
|  | ||||
| 			<!-- (UTC+11:00) Sakhalin --> | ||||
| 			<mapZone other="Sakhalin Standard Time" territory="001" type="Asia/Sakhalin"/> | ||||
| 			<mapZone other="Sakhalin Standard Time" territory="RU" type="Asia/Sakhalin"/> | ||||
|  | ||||
| 			<!-- (UTC+11:00) Solomon Is., New Caledonia --> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="001" type="Pacific/Guadalcanal"/> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="AQ" type="Antarctica/Casey"/> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="AU" type="Antarctica/Macquarie"/> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="FM" type="Pacific/Ponape Pacific/Kosrae"/> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="NC" type="Pacific/Noumea"/> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="SB" type="Pacific/Guadalcanal"/> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="VU" type="Pacific/Efate"/> | ||||
| 			<mapZone other="Central Pacific Standard Time" territory="ZZ" type="Etc/GMT-11"/> | ||||
|  | ||||
| 			<!-- (UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky --> | ||||
| 			<mapZone other="Russia Time Zone 11" territory="001" type="Asia/Kamchatka"/> | ||||
| 			<mapZone other="Russia Time Zone 11" territory="RU" type="Asia/Kamchatka Asia/Anadyr"/> | ||||
|  | ||||
| 			<!-- (UTC+12:00) Auckland, Wellington --> | ||||
| 			<mapZone other="New Zealand Standard Time" territory="001" type="Pacific/Auckland"/> | ||||
| 			<mapZone other="New Zealand Standard Time" territory="AQ" type="Antarctica/McMurdo"/> | ||||
| 			<mapZone other="New Zealand Standard Time" territory="NZ" type="Pacific/Auckland"/> | ||||
|  | ||||
| 			<!-- (UTC+12:00) Coordinated Universal Time+12 --> | ||||
| 			<mapZone other="UTC+12" territory="001" type="Etc/GMT-12"/> | ||||
| 			<mapZone other="UTC+12" territory="KI" type="Pacific/Tarawa"/> | ||||
| 			<mapZone other="UTC+12" territory="MH" type="Pacific/Majuro Pacific/Kwajalein"/> | ||||
| 			<mapZone other="UTC+12" territory="NR" type="Pacific/Nauru"/> | ||||
| 			<mapZone other="UTC+12" territory="TV" type="Pacific/Funafuti"/> | ||||
| 			<mapZone other="UTC+12" territory="UM" type="Pacific/Wake"/> | ||||
| 			<mapZone other="UTC+12" territory="WF" type="Pacific/Wallis"/> | ||||
| 			<mapZone other="UTC+12" territory="ZZ" type="Etc/GMT-12"/> | ||||
|  | ||||
| 			<!-- (UTC+12:00) Fiji --> | ||||
| 			<mapZone other="Fiji Standard Time" territory="001" type="Pacific/Fiji"/> | ||||
| 			<mapZone other="Fiji Standard Time" territory="FJ" type="Pacific/Fiji"/> | ||||
|  | ||||
| 			<!-- (UTC+12:45) Chatham Islands --> | ||||
| 			<mapZone other="Chatham Islands Standard Time" territory="001" type="Pacific/Chatham"/> | ||||
| 			<mapZone other="Chatham Islands Standard Time" territory="NZ" type="Pacific/Chatham"/> | ||||
|  | ||||
| 			<!-- (UTC+13:00) Nuku'alofa --> | ||||
| 			<mapZone other="Tonga Standard Time" territory="001" type="Pacific/Tongatapu"/> | ||||
| 			<mapZone other="Tonga Standard Time" territory="KI" type="Pacific/Enderbury"/> | ||||
| 			<mapZone other="Tonga Standard Time" territory="TK" type="Pacific/Fakaofo"/> | ||||
| 			<mapZone other="Tonga Standard Time" territory="TO" type="Pacific/Tongatapu"/> | ||||
| 			<mapZone other="Tonga Standard Time" territory="ZZ" type="Etc/GMT-13"/> | ||||
|  | ||||
| 			<!-- (UTC+13:00) Samoa --> | ||||
| 			<mapZone other="Samoa Standard Time" territory="001" type="Pacific/Apia"/> | ||||
| 			<mapZone other="Samoa Standard Time" territory="WS" type="Pacific/Apia"/> | ||||
|  | ||||
| 			<!-- (UTC+14:00) Kiritimati Island --> | ||||
| 			<mapZone other="Line Islands Standard Time" territory="001" type="Pacific/Kiritimati"/> | ||||
| 			<mapZone other="Line Islands Standard Time" territory="KI" type="Pacific/Kiritimati"/> | ||||
| 			<mapZone other="Line Islands Standard Time" territory="ZZ" type="Etc/GMT-14"/> | ||||
| 		</mapTimezones> | ||||
| 	</windowsZones> | ||||
| </supplementalData> | ||||
							
								
								
									
										2
									
								
								externals/phpmailer/class.phpmailer.php
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1110,6 +1110,8 @@ class PHPMailer { | ||||
|  | ||||
|     if($this->MessageID != '') { | ||||
|       $result .= $this->HeaderLine('Message-ID',$this->MessageID); | ||||
|     } else { | ||||
|       $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); | ||||
|     } | ||||
|     $result .= $this->HeaderLine('X-Priority', $this->Priority); | ||||
|     $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.sourceforge.net)'); | ||||
|   | ||||
							
								
								
									
										610
									
								
								migration/dedup.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,610 @@ | ||||
| <?php | ||||
|  | ||||
| $migrate_dedup_users = array(); | ||||
|  | ||||
| $migrate_dedup_users["midiclub"] = "midclub"; | ||||
| $migrate_dedup_users["trip"] = "car"; | ||||
| $migrate_dedup_users["thunderbolt"] = "thunderbolt16"; | ||||
| $migrate_dedup_users["avirnig"] = "beav"; | ||||
| $migrate_dedup_users["jeaninmontreal"] = "jeanm"; | ||||
| $migrate_dedup_users["nneo"] = "nneonneo"; | ||||
| $migrate_dedup_users["ebonyj"] = "jim"; | ||||
| $migrate_dedup_users["tnr"] = "ichem"; | ||||
| $migrate_dedup_users["archus"] = "rstehwien"; | ||||
| $migrate_dedup_users["kalysto"] = "kalar"; | ||||
| $migrate_dedup_users["eloe"] = "mouvanteloe"; | ||||
| $migrate_dedup_users["erdling"] = "eitj"; | ||||
| $migrate_dedup_users["cwinebrinner"] = "chuck_1027"; | ||||
| $migrate_dedup_users["foo"] = "mfranz"; | ||||
| $migrate_dedup_users["f00fbug"] = "viviowns"; | ||||
| $migrate_dedup_users["olavbalm"] = "olav"; | ||||
| $migrate_dedup_users["kapong"] = "el_kapong"; | ||||
| $migrate_dedup_users["thorax"] = "rofthorax"; | ||||
| $migrate_dedup_users["kramer3d"] = "asdfghytrewq"; | ||||
| $migrate_dedup_users["reevee"] = "vee"; | ||||
| $migrate_dedup_users["matzeb"] = "matzebraun"; | ||||
| $migrate_dedup_users["jmsoler"] = "jms"; | ||||
| $migrate_dedup_users["aqa"] = "angelito"; | ||||
| $migrate_dedup_users["blendorphin"] = "blenergetic"; | ||||
| $migrate_dedup_users["paolo"] = "jody"; | ||||
| $migrate_dedup_users["blendedidea"] = "chubango"; | ||||
| $migrate_dedup_users["ruiramos"] = "p3ctu5"; | ||||
| $migrate_dedup_users["head_chees"] = "vassilizaitsaf"; | ||||
| $migrate_dedup_users["kiernan"] = "rofthorax"; | ||||
| $migrate_dedup_users["olaf_arnold"] = "olafarnold"; | ||||
| $migrate_dedup_users["joseyanesaltos"] = "joseyanes"; | ||||
| $migrate_dedup_users["nicholasfrancis"] = "nicholas"; | ||||
| $migrate_dedup_users["briancary"] = "brinux"; | ||||
| $migrate_dedup_users["piotrek"] = "ccpiotr"; | ||||
| $migrate_dedup_users["slashdev"] = "colonel_panic"; | ||||
| $migrate_dedup_users["rj2004"] = "rjblender"; | ||||
| $migrate_dedup_users["jeffreymcgrew"] = "toast"; | ||||
| $migrate_dedup_users["hendersj"] = "jhenderson"; | ||||
| $migrate_dedup_users["pierpy"] = "pier"; | ||||
| $migrate_dedup_users["buschhardt"] = "ishtar"; | ||||
| $migrate_dedup_users["timlesher"] = "tim"; | ||||
| $migrate_dedup_users["tizmo"] = "timlucas"; | ||||
| $migrate_dedup_users["pprandin"] = "pashaweb"; | ||||
| $migrate_dedup_users["servivo"] = "taz"; | ||||
| $migrate_dedup_users["stabby"] = "xingo"; | ||||
| $migrate_dedup_users["jimmydietulpe"] = "jimmadietulpe"; | ||||
| $migrate_dedup_users["palmnet"] = "palmer"; | ||||
| $migrate_dedup_users["graphique-chti"] = "gcprod"; | ||||
| $migrate_dedup_users["dreamkatana1"] = "dreamkatana"; | ||||
| $migrate_dedup_users["wolverine"] = "shane"; | ||||
| $migrate_dedup_users["schwarz"] = "wene"; | ||||
| $migrate_dedup_users["justtesting"] = "levon"; | ||||
| $migrate_dedup_users["erwin_xnan"] = "erwin"; | ||||
| $migrate_dedup_users["zack"] = "krystof"; | ||||
| $migrate_dedup_users["fabinator31"] = "fab31"; | ||||
| $migrate_dedup_users["themilkman"] = "cliftonium"; | ||||
| $migrate_dedup_users["etiennel"] = "choupi"; | ||||
| $migrate_dedup_users["m_i_c_h_e_l"] = "michaels"; | ||||
| $migrate_dedup_users["techrolla"] = "muffinpeddler"; | ||||
| $migrate_dedup_users["fireflymantis"] = "twilight"; | ||||
| $migrate_dedup_users["pinucset0"] = "pinucset"; | ||||
| $migrate_dedup_users["franzrogar"] = "blender-i18n-po"; | ||||
| $migrate_dedup_users["pinucset1"] = "pinucset"; | ||||
| $migrate_dedup_users["pinucset3"] = "pinucset"; | ||||
| $migrate_dedup_users["threeddream"] = "x-axis"; | ||||
| $migrate_dedup_users["mudbot"] = "laurence"; | ||||
| $migrate_dedup_users["ligeyx"] = "ligey"; | ||||
| $migrate_dedup_users["dreas"] = "dre"; | ||||
| $migrate_dedup_users["joorvapl"] = "joorva"; | ||||
| $migrate_dedup_users["voyage"] = "xenithi"; | ||||
| $migrate_dedup_users["azter"] = "azterix"; | ||||
| $migrate_dedup_users["lapinbleu"] = "simeon"; | ||||
| $migrate_dedup_users["emmanuel_t"] = "emmanuel"; | ||||
| $migrate_dedup_users["cricket"] = "gku"; | ||||
| $migrate_dedup_users["justanumber"] = "mad4linux"; | ||||
| $migrate_dedup_users["johnnyen"] = "johnny"; | ||||
| $migrate_dedup_users["leiota"] = "marcopolo"; | ||||
| $migrate_dedup_users["blackstorm"] = "ikbendirk"; | ||||
| $migrate_dedup_users["richie"] = "ligey"; | ||||
| $migrate_dedup_users["mrcarnivore2"] = "mrcarnivore"; | ||||
| $migrate_dedup_users["muddym1nd"] = "muddymind"; | ||||
| $migrate_dedup_users["eumesmo"] = "vasco_da_gama"; | ||||
| $migrate_dedup_users["ph-kby"] = "olav"; | ||||
| $migrate_dedup_users["piotrek_"] = "ccpiotr"; | ||||
| $migrate_dedup_users["joaquin"] = "prospero"; | ||||
| $migrate_dedup_users["konrad_haenel"] = "konrad8ha"; | ||||
| $migrate_dedup_users["benstabler"] = "lightning"; | ||||
| $migrate_dedup_users["gustav"] = "draknoid"; | ||||
| $migrate_dedup_users["slikdigit"] = "bassamk"; | ||||
| $migrate_dedup_users["karlerlandsen"] = "lethalsidep"; | ||||
| $migrate_dedup_users["djwillmsoh"] = "djw1"; | ||||
| $migrate_dedup_users["aschmitz"] = "root_42"; | ||||
| $migrate_dedup_users["malfunc"] = "mcq"; | ||||
| $migrate_dedup_users["m_jack"] = "jack"; | ||||
| $migrate_dedup_users["davey"] = "madcow"; | ||||
| $migrate_dedup_users["iraytrace"] = "butler"; | ||||
| $migrate_dedup_users["duser"] = "mslechols"; | ||||
| $migrate_dedup_users["akito"] = "ton"; | ||||
| $migrate_dedup_users["jochenschmitt"] = "s4504kr"; | ||||
| $migrate_dedup_users["digikiller"] = "ace1"; | ||||
| $migrate_dedup_users["shzhc"] = "zzzz"; | ||||
| $migrate_dedup_users["cyrilbrulebois"] = "kibi"; | ||||
| $migrate_dedup_users["themushroom"] = "draknoid"; | ||||
| $migrate_dedup_users["zinkmaster"] = "mariolink"; | ||||
| $migrate_dedup_users["ashsid"] = "ash"; | ||||
| $migrate_dedup_users["novato"] = "jimenez"; | ||||
| $migrate_dedup_users["jadie_p"] = "jadie"; | ||||
| $migrate_dedup_users["neshshah007"] = "neshrai"; | ||||
| $migrate_dedup_users["ryanshow"] = "tankcoder"; | ||||
| $migrate_dedup_users["squarelinesq"] = "squareline"; | ||||
| $migrate_dedup_users["virginijus"] = "ernestas1994"; | ||||
| $migrate_dedup_users["renderdemon"] = "pnio"; | ||||
| $migrate_dedup_users["kwab"] = "kobi"; | ||||
| $migrate_dedup_users["peterjonckheere"] = "jonckheerep"; | ||||
| $migrate_dedup_users["vidar"] = "vidarino"; | ||||
| $migrate_dedup_users["sapjohannes"] = "johannesje"; | ||||
| $migrate_dedup_users["youri"] = "ayers"; | ||||
| $migrate_dedup_users["theamiman"] = "amiman"; | ||||
| $migrate_dedup_users["chrisg_ksi"] = "red_planet"; | ||||
| $migrate_dedup_users["blenderorgfan"] = "franciscosilva"; | ||||
| $migrate_dedup_users["ericd"] = "ericdrayer"; | ||||
| $migrate_dedup_users["asdfghjkl2"] = "asdfghjkl"; | ||||
| $migrate_dedup_users["quorn"] = "alienit"; | ||||
| $migrate_dedup_users["daan221"] = "phoenix221"; | ||||
| $migrate_dedup_users["user"] = "mslechols"; | ||||
| $migrate_dedup_users["pyb"] = "lomperillo"; | ||||
| $migrate_dedup_users["g56"] = "gwald"; | ||||
| $migrate_dedup_users["ausland"] = "vots"; | ||||
| $migrate_dedup_users["quillinan"] = "orinoco"; | ||||
| $migrate_dedup_users["t3tsuj1n"] = "bootsheoz"; | ||||
| $migrate_dedup_users["molflesh"] = "melflash"; | ||||
| $migrate_dedup_users["rickta59"] = "aerobicsboy"; | ||||
| $migrate_dedup_users["nolopoly"] = "lopoly"; | ||||
| $migrate_dedup_users["jeel"] = "jl57"; | ||||
| $migrate_dedup_users["brianmccumber2"] = "brianmccumber"; | ||||
| $migrate_dedup_users["mouette"] = "moutte"; | ||||
| $migrate_dedup_users["wout"] = "wonderingwout"; | ||||
| $migrate_dedup_users["fatfinger"] = "mattyc"; | ||||
| $migrate_dedup_users["nathangull"] = "nateg"; | ||||
| $migrate_dedup_users["tampadave"] = "dkmweeks"; | ||||
| $migrate_dedup_users["sausages"] = "lethargic"; | ||||
| $migrate_dedup_users["chris3d"] = "chrisbd"; | ||||
| $migrate_dedup_users["kaito"] = "ton"; | ||||
| $migrate_dedup_users["akirasan"] = "akira_b"; | ||||
| $migrate_dedup_users["fiddle"] = "rayf"; | ||||
| $migrate_dedup_users["bald"] = "manu"; | ||||
| $migrate_dedup_users["mistermelquin"] = "melquin"; | ||||
| $migrate_dedup_users["kofish"] = "kingdomoffish"; | ||||
| $migrate_dedup_users["onanj"] = "orangee"; | ||||
| $migrate_dedup_users["jasoncarrier"] = "jason"; | ||||
| $migrate_dedup_users["joelgodin"] = "jenniferblender"; | ||||
| $migrate_dedup_users["oranj"] = "orangee"; | ||||
| $migrate_dedup_users["schdeb"] = "schmidtcristian"; | ||||
| $migrate_dedup_users["bastian"] = "angerb"; | ||||
| $migrate_dedup_users["patko"] = "patco"; | ||||
| $migrate_dedup_users["arakyd666"] = "arakyd"; | ||||
| $migrate_dedup_users["karimf"] = "rim-k"; | ||||
| $migrate_dedup_users["nletwory"] = "jestertestest"; | ||||
| $migrate_dedup_users["mansoorhyder"] = "mansoor"; | ||||
| $migrate_dedup_users["rocketmagnet"] = "steventr"; | ||||
| $migrate_dedup_users["thekernal"] = "kernal"; | ||||
| $migrate_dedup_users["satom"] = "satriatomat"; | ||||
| $migrate_dedup_users["a624"] = "a623"; | ||||
| $migrate_dedup_users["jldavilacasares"] = "muki"; | ||||
| $migrate_dedup_users["segersj"] = "segers"; | ||||
| $migrate_dedup_users["hoisan49"] = "hoisan"; | ||||
| $migrate_dedup_users["j_bessette"] = "linuxpimp20"; | ||||
| $migrate_dedup_users["andreas999"] = "andreas"; | ||||
| $migrate_dedup_users["dusan"] = "dsc512"; | ||||
| $migrate_dedup_users["dx-mon"] = "mantr100"; | ||||
| $migrate_dedup_users["ilislab"] = "ids"; | ||||
| $migrate_dedup_users["rben"] = "raybenjamin"; | ||||
| $migrate_dedup_users["sphere"] = "dysonsphere"; | ||||
| $migrate_dedup_users["highlife22"] = "highlife"; | ||||
| $migrate_dedup_users["yonarw"] = "yona"; | ||||
| $migrate_dedup_users["kryptic89"] = "kryptic"; | ||||
| $migrate_dedup_users["axelp"] = "axel"; | ||||
| $migrate_dedup_users["perebalsach"] = "fog22"; | ||||
| $migrate_dedup_users["alogerson"] = "gerson"; | ||||
| $migrate_dedup_users["cotejrp"] = "cotejrp1"; | ||||
| $migrate_dedup_users["tak"] = "carlosjamesr"; | ||||
| $migrate_dedup_users["ziddy"] = "anishchandran"; | ||||
| $migrate_dedup_users["asdgz"] = "blenderiseur"; | ||||
| $migrate_dedup_users["cdated"] = "cdated257"; | ||||
| $migrate_dedup_users["jayrkalugin"] = "jayr"; | ||||
| $migrate_dedup_users["zeemzoet"] = "johannesje"; | ||||
| $migrate_dedup_users["duo"] = "ambyra"; | ||||
| $migrate_dedup_users["philb"] = "chewbacca"; | ||||
| $migrate_dedup_users["tzi"] = "izt"; | ||||
| $migrate_dedup_users["squirrelthetire"] = "squirrel-tire"; | ||||
| $migrate_dedup_users["anwyn"] = "sugarshark"; | ||||
| $migrate_dedup_users["a2z"] = "a2zaa"; | ||||
| $migrate_dedup_users["fxrex"] = "femi"; | ||||
| $migrate_dedup_users["alethewiz"] = "mfaso68"; | ||||
| $migrate_dedup_users["noseferrit"] = "billybong"; | ||||
| $migrate_dedup_users["fcali"] = "fabz"; | ||||
| $migrate_dedup_users["bbirchler"] = "bblender"; | ||||
| $migrate_dedup_users["oslosewers"] = "oslo"; | ||||
| $migrate_dedup_users["wtrsltnk"] = "wtr"; | ||||
| $migrate_dedup_users["cspohst"] = "spohst"; | ||||
| $migrate_dedup_users["warhawk08"] = "warhawk1990"; | ||||
| $migrate_dedup_users["magick_crow"] = "magickcrow"; | ||||
| $migrate_dedup_users["guillaumem"] = "guillaume"; | ||||
| $migrate_dedup_users["jwitthuhn"] = "rahu"; | ||||
| $migrate_dedup_users["fmehigan"] = "frank_me"; | ||||
| $migrate_dedup_users["ilissys"] = "ids"; | ||||
| $migrate_dedup_users["supermoaaa"] = "moaaa"; | ||||
| $migrate_dedup_users["nwmatt"] = "mhenley"; | ||||
| $migrate_dedup_users["bezel"] = "xshell"; | ||||
| $migrate_dedup_users["rebuss"] = "studioa"; | ||||
| $migrate_dedup_users["geonom"] = "geoadel"; | ||||
| $migrate_dedup_users["seldan"] = "farakon"; | ||||
| $migrate_dedup_users["kuru76"] = "kcorbin"; | ||||
| $migrate_dedup_users["kapil"] = "kapilbedarkar"; | ||||
| $migrate_dedup_users["henryiii"] = "henryschreiner"; | ||||
| $migrate_dedup_users["pateamcarl"] = "carlhuth"; | ||||
| $migrate_dedup_users["kamen"] = "bigbob1993"; | ||||
| $migrate_dedup_users["olddemon"] = "old_demon"; | ||||
| $migrate_dedup_users["draklaw2"] = "draklaw"; | ||||
| $migrate_dedup_users["ksdlee"] = "kdlee"; | ||||
| $migrate_dedup_users["xource"] = "admix"; | ||||
| $migrate_dedup_users["invertednormal"] = "smokebox46and2"; | ||||
| $migrate_dedup_users["feelgoodcomics"] = "onlygoodwin"; | ||||
| $migrate_dedup_users["glorund"] = "undolaure"; | ||||
| $migrate_dedup_users["soulofsound"] = "johnnym"; | ||||
| $migrate_dedup_users["ghigi123"] = "ghigi"; | ||||
| $migrate_dedup_users["stefanvoigthpi"] = "derstefan"; | ||||
| $migrate_dedup_users["dblenderv"] = "default"; | ||||
| $migrate_dedup_users["ophiocus"] = "oneliner"; | ||||
| $migrate_dedup_users["colleywrks"] = "colley"; | ||||
| $migrate_dedup_users["fogy"] = "fog22"; | ||||
| $migrate_dedup_users["vidarn"] = "bida70"; | ||||
| $migrate_dedup_users["kevin"] = "goblender2541"; | ||||
| $migrate_dedup_users["tisachris"] = "warflight"; | ||||
| $migrate_dedup_users["personalex2"] = "personalex"; | ||||
| $migrate_dedup_users["polypa"] = "lile"; | ||||
| $migrate_dedup_users["i4dnf"] = "emilian"; | ||||
| $migrate_dedup_users["nitalleb"] = "singleman"; | ||||
| $migrate_dedup_users["tcgodoy"] = "godoy"; | ||||
| $migrate_dedup_users["joorva_pl"] = "joorva"; | ||||
| $migrate_dedup_users["sex"] = "xest"; | ||||
| $migrate_dedup_users["chipmunk"] = "ignatz"; | ||||
| $migrate_dedup_users["schalmagne"] = "chalmagne"; | ||||
| $migrate_dedup_users["michalziulek"] = "eneida"; | ||||
| $migrate_dedup_users["jeeps"] = "jeepster"; | ||||
| $migrate_dedup_users["yain"] = "chaos"; | ||||
| $migrate_dedup_users["amadio"] = "marble"; | ||||
| $migrate_dedup_users["javiere"] = "javierchavez"; | ||||
| $migrate_dedup_users["drmzperx"] = "mzperx"; | ||||
| $migrate_dedup_users["sebastianreaser"] = "sebastian0"; | ||||
| $migrate_dedup_users["idolon"] = "spadija"; | ||||
| $migrate_dedup_users["alanhzhcn"] = "alanhzh"; | ||||
| $migrate_dedup_users["jeanc"] = "jean"; | ||||
| $migrate_dedup_users["transblue2"] = "transblue"; | ||||
| $migrate_dedup_users["cesarwilfredo"] = "cesar"; | ||||
| $migrate_dedup_users["nspyr"] = "spiderfire"; | ||||
| $migrate_dedup_users["supermegamoaaa"] = "moaaa"; | ||||
| $migrate_dedup_users["firetiger"] = "opensolution"; | ||||
| $migrate_dedup_users["skarg"] = "cados"; | ||||
| $migrate_dedup_users["realheadcrusher"] = "danigr"; | ||||
| $migrate_dedup_users["kpg"] = "nihylius"; | ||||
| $migrate_dedup_users["tft"] = "tft67"; | ||||
| $migrate_dedup_users["storabbarn"] = "morre"; | ||||
| $migrate_dedup_users["bacurau"] = "roger_roo"; | ||||
| $migrate_dedup_users["alisari"] = "parasoley"; | ||||
| $migrate_dedup_users["skiski"] = "superrom"; | ||||
| $migrate_dedup_users["halfninja"] = "nickh"; | ||||
| $migrate_dedup_users["drell_develop"] = "drellex"; | ||||
| $migrate_dedup_users["highlif3"] = "highlife"; | ||||
| $migrate_dedup_users["broggsim1"] = "broggsim"; | ||||
| $migrate_dedup_users["blendermanuci"] = "yunior88"; | ||||
| $migrate_dedup_users["jpt9"] = "jtuttle"; | ||||
| $migrate_dedup_users["farrer"] = "farpro"; | ||||
| $migrate_dedup_users["badcheez"] = "randall"; | ||||
| $migrate_dedup_users["leewj_"] = "lohns"; | ||||
| $migrate_dedup_users["martin107"] = "martinfrances"; | ||||
| $migrate_dedup_users["dedpan"] = "tberghuis"; | ||||
| $migrate_dedup_users["whiterabbit"] = "dreamscapearts"; | ||||
| $migrate_dedup_users["shuvro"] = "shuvro05"; | ||||
| $migrate_dedup_users["anichandru"] = "anishchandran"; | ||||
| $migrate_dedup_users["ncaralph"] = "ralphdoctorow"; | ||||
| $migrate_dedup_users["nlongchamps"] = "nlong"; | ||||
| $migrate_dedup_users["mawi37"] = "mawi"; | ||||
| $migrate_dedup_users["rizla"] = "jay"; | ||||
| $migrate_dedup_users["jeh"] = "mirkril"; | ||||
| $migrate_dedup_users["b-fighter"] = "fanatic"; | ||||
| $migrate_dedup_users["wasamonkey"] = "wasa"; | ||||
| $migrate_dedup_users["nicola"] = "martin45"; | ||||
| $migrate_dedup_users["method-es"] = "method"; | ||||
| $migrate_dedup_users["pablow"] = "warhole"; | ||||
| $migrate_dedup_users["alanishzh"] = "alanhzh"; | ||||
| $migrate_dedup_users["thunder947"] = "thunder"; | ||||
| $migrate_dedup_users["yijimi"] = "roler"; | ||||
| $migrate_dedup_users["jaydenb"] = "logidude"; | ||||
| $migrate_dedup_users["leandropolus"] = "leandrosz"; | ||||
| $migrate_dedup_users["ibi002"] = "ibi001"; | ||||
| $migrate_dedup_users["hakanortasoz"] = "tayfax"; | ||||
| $migrate_dedup_users["enur"] = "rune"; | ||||
| $migrate_dedup_users["dsuesse"] = "qiip"; | ||||
| $migrate_dedup_users["bipedlaboratory"] = "redmetal"; | ||||
| $migrate_dedup_users["codeyhanson"] = "codey"; | ||||
| $migrate_dedup_users["alphonso_b"] = "alfonso_b"; | ||||
| $migrate_dedup_users["surreal6"] = "carlospadial"; | ||||
| $migrate_dedup_users["kralizek"] = "kral"; | ||||
| $migrate_dedup_users["kfrechet"] = "keithfr"; | ||||
| $migrate_dedup_users["jwedlake"] = "joshwedlake"; | ||||
| $migrate_dedup_users["westie630"] = "bully"; | ||||
| $migrate_dedup_users["fictional"] = "icefire"; | ||||
| $migrate_dedup_users["zelozelos"] = "zelozelos1"; | ||||
| $migrate_dedup_users["dragonlord"] = "acuena"; | ||||
| $migrate_dedup_users["mrcheese"] = "jpeg"; | ||||
| $migrate_dedup_users["willemverwey"] = "dandandan"; | ||||
| $migrate_dedup_users["jhed"] = "anton_foy"; | ||||
| $migrate_dedup_users["treacy1077"] = "briant"; | ||||
| $migrate_dedup_users["xest"] = "xembie"; | ||||
| $migrate_dedup_users["cyphl25"] = "jmsfreezer"; | ||||
| $migrate_dedup_users["wynk"] = "wynn"; | ||||
| $migrate_dedup_users["trock2957"] = "trock"; | ||||
| $migrate_dedup_users["mr_bomb"] = "carter24"; | ||||
| $migrate_dedup_users["nikolaus"] = "tortellini"; | ||||
| $migrate_dedup_users["pegasus_001"] = "pegasus001"; | ||||
| $migrate_dedup_users["fayte"] = "fayte220"; | ||||
| $migrate_dedup_users["jagang_8"] = "jagang"; | ||||
| $migrate_dedup_users["thetwom"] = "moe"; | ||||
| $migrate_dedup_users["kevlareditor"] = "klthomas"; | ||||
| $migrate_dedup_users["damien_deom"] = "dams"; | ||||
| $migrate_dedup_users["reynantem"] = "reynante"; | ||||
| $migrate_dedup_users["hiralm01"] = "hiralm"; | ||||
| $migrate_dedup_users["radix7"] = "wyldethang"; | ||||
| $migrate_dedup_users["puppetm"] = "puppetmaster"; | ||||
| $migrate_dedup_users["vimax"] = "vimaxus"; | ||||
| $migrate_dedup_users["mozzy69"] = "lyndon"; | ||||
| $migrate_dedup_users["vitranaccad"] = "thestorm74"; | ||||
| $migrate_dedup_users["kj12345"] = "kevinjames"; | ||||
| $migrate_dedup_users["foinix"] = "mrnoodle"; | ||||
| $migrate_dedup_users["shizu"] = "sntulix"; | ||||
| $migrate_dedup_users["kukulcangod23"] = "kukulcangod"; | ||||
| $migrate_dedup_users["piliq"] = "qpblendpolis"; | ||||
| $migrate_dedup_users["benvad"] = "vbenny"; | ||||
| $migrate_dedup_users["fgribben"] = "sharkey"; | ||||
| $migrate_dedup_users["makospince"] = "mako_spince"; | ||||
| $migrate_dedup_users["ablenderuser"] = "pancakeface"; | ||||
| $migrate_dedup_users["axelphi"] = "axel"; | ||||
| $migrate_dedup_users["josiasbh"] = "josias"; | ||||
| $migrate_dedup_users["mattsix"] = "majawe"; | ||||
| $migrate_dedup_users["duarte_ramos"] = "dphantom"; | ||||
| $migrate_dedup_users["killogge"] = "muraj"; | ||||
| $migrate_dedup_users["luka"] = "omgwtfbbq"; | ||||
| $migrate_dedup_users["chromemonkey"] = "brian651msp"; | ||||
| $migrate_dedup_users["e_d_i"] = "ide"; | ||||
| $migrate_dedup_users["mrhaynesy"] = "haynesy"; | ||||
| $migrate_dedup_users["imagineering"] = "imagineer"; | ||||
| $migrate_dedup_users["virgiliovasconc"] = "virgilio"; | ||||
| $migrate_dedup_users["blenderlb57"] = "louigi"; | ||||
| $migrate_dedup_users["nick65"] = "dgnicola"; | ||||
| $migrate_dedup_users["mmatthews"] = "mamatthews"; | ||||
| $migrate_dedup_users["gerwood"] = "arilian"; | ||||
| $migrate_dedup_users["xsidmax"] = "xsi"; | ||||
| $migrate_dedup_users["mix-yag"] = "aynahsim"; | ||||
| $migrate_dedup_users["wpthomas"] = "tallguy"; | ||||
| $migrate_dedup_users["bp007"] = "bender007"; | ||||
| $migrate_dedup_users["emach"] = "eon4blender"; | ||||
| $migrate_dedup_users["alfclement"] = "alfc"; | ||||
| $migrate_dedup_users["blenderwell"] = "goplexian"; | ||||
| $migrate_dedup_users["asebastianr"] = "sebastian0"; | ||||
| $migrate_dedup_users["joepal1976"] = "joepal"; | ||||
| $migrate_dedup_users["chertov"] = "lestat"; | ||||
| $migrate_dedup_users["djela63"] = "jerominovich"; | ||||
| $migrate_dedup_users["lppinto"] = "lpciper"; | ||||
| $migrate_dedup_users["prana"] = "nomath"; | ||||
| $migrate_dedup_users["sarubadoru"] = "salvador"; | ||||
| $migrate_dedup_users["rarebit"] = "rawstar7"; | ||||
| $migrate_dedup_users["hikikamori"] = "hikkikamori"; | ||||
| $migrate_dedup_users["mrduke"] = "drop"; | ||||
| $migrate_dedup_users["deetee"] = "dertee"; | ||||
| $migrate_dedup_users["woooooah"] = "noxwell"; | ||||
| $migrate_dedup_users["macoss"] = "ossmac"; | ||||
| $migrate_dedup_users["bunnyboy212"] = "blender_buddie"; | ||||
| $migrate_dedup_users["maxgrip"] = "czarek"; | ||||
| $migrate_dedup_users["tentonman"] = "titandtat"; | ||||
| $migrate_dedup_users["jabozzo"] = "bozzo"; | ||||
| $migrate_dedup_users["reinways"] = "reinw"; | ||||
| $migrate_dedup_users["petergk"] = "nihylius"; | ||||
| $migrate_dedup_users["zphr3000"] = "zphr"; | ||||
| $migrate_dedup_users["ruivo"] = "andreh"; | ||||
| $migrate_dedup_users["kosta"] = "kgd"; | ||||
| $migrate_dedup_users["delter"] = "dertee"; | ||||
| $migrate_dedup_users["jmiller"] = "lethargic"; | ||||
| $migrate_dedup_users["dealga"] = "zeffii"; | ||||
| $migrate_dedup_users["bogey"] = "daveh"; | ||||
| $migrate_dedup_users["silencebe"] = "silence"; | ||||
| $migrate_dedup_users["temozarela"] = "gorn"; | ||||
| $migrate_dedup_users["tischite"] = "greenbutton"; | ||||
| $migrate_dedup_users["buisson"] = "dinodino"; | ||||
| $migrate_dedup_users["cfox"] = "colinfox"; | ||||
| $migrate_dedup_users["hunkadoodle"] = "hunkadoodledoo"; | ||||
| $migrate_dedup_users["jlwitthuhn"] = "rahu"; | ||||
| $migrate_dedup_users["hvfrancesco"] = "hva"; | ||||
| $migrate_dedup_users["hazim-jamal"] = "hazim1"; | ||||
| $migrate_dedup_users["aurosutru"] = "tlm"; | ||||
| $migrate_dedup_users["pierrea"] = "pier2"; | ||||
| $migrate_dedup_users["zoon"] = "zoonpolygonikon"; | ||||
| $migrate_dedup_users["gruntbatch"] = "carmichael"; | ||||
| $migrate_dedup_users["petru"] = "virusanti"; | ||||
| $migrate_dedup_users["mikeh74"] = "mikeh"; | ||||
| $migrate_dedup_users["sugoi"] = "juntunen"; | ||||
| $migrate_dedup_users["bartje"] = "bartart3d"; | ||||
| $migrate_dedup_users["yaoyansi"] = "yaoyansi2"; | ||||
| $migrate_dedup_users["rafek"] = "rafek_finearts"; | ||||
| $migrate_dedup_users["caspern"] = "caspernilsson"; | ||||
| $migrate_dedup_users["lee"] = "s_random"; | ||||
| $migrate_dedup_users["sparky"] = "mmikkelsen"; | ||||
| $migrate_dedup_users["wigglyframes"] = "rene"; | ||||
| $migrate_dedup_users["kroni"] = "kronos"; | ||||
| $migrate_dedup_users["xgl_asyliax"] = "xglasyliax"; | ||||
| $migrate_dedup_users["blendeador"] = "luisperosio"; | ||||
| $migrate_dedup_users["kaos1986"] = "kaos86"; | ||||
| $migrate_dedup_users["adamdoyle"] = "advs89"; | ||||
| $migrate_dedup_users["kahenraz"] = "mistrel"; | ||||
| $migrate_dedup_users["ccliffe"] = "cjcliffe"; | ||||
| $migrate_dedup_users["nullfied"] = "xercesblue"; | ||||
| $migrate_dedup_users["dustyghost"] = "dustbin1_uk"; | ||||
| $migrate_dedup_users["claaskuhnen"] = "cekuhnendev"; | ||||
| $migrate_dedup_users["vercingetorix"] = "diamond_rust"; | ||||
| $migrate_dedup_users["plugboy"] = "centerlaw"; | ||||
| $migrate_dedup_users["rben13"] = "raybenjamin"; | ||||
| $migrate_dedup_users["deangiberson"] = "voidptr"; | ||||
| $migrate_dedup_users["makeitwork"] = "bboybram"; | ||||
| $migrate_dedup_users["iljosli"] = "jos"; | ||||
| $migrate_dedup_users["slowan"] = "slovan"; | ||||
| $migrate_dedup_users["mooonwalkercz"] = "mooonwalker"; | ||||
| $migrate_dedup_users["tapplek"] = "tapple"; | ||||
| $migrate_dedup_users["blendmond"] = "cornix"; | ||||
| $migrate_dedup_users["giorgiomartini"] = "tweakingknobs"; | ||||
| $migrate_dedup_users["davidray"] = "deadman"; | ||||
| $migrate_dedup_users["justthisguy"] = "nyctef"; | ||||
| $migrate_dedup_users["logobar"] = "freyer"; | ||||
| $migrate_dedup_users["zooo"] = "leon_cheung"; | ||||
| $migrate_dedup_users["josiasalexandre"] = "josias"; | ||||
| $migrate_dedup_users["tsukiko_chan"] = "tsukikochan"; | ||||
| $migrate_dedup_users["akira_san"] = "akira_b"; | ||||
| $migrate_dedup_users["walberti"] = "walbertievarist"; | ||||
| $migrate_dedup_users["astro"] = "tnboma"; | ||||
| $migrate_dedup_users["mccx"] = "mcq"; | ||||
| $migrate_dedup_users["daa84"] = "daa"; | ||||
| $migrate_dedup_users["sivert3"] = "cent"; | ||||
| $migrate_dedup_users["twentyone"] = "glade"; | ||||
| $migrate_dedup_users["endi2"] = "endi"; | ||||
| $migrate_dedup_users["jamesr"] = "james_r"; | ||||
| $migrate_dedup_users["reece"] = "reecerobinson"; | ||||
| $migrate_dedup_users["dustractor"] = "bpygrams"; | ||||
| $migrate_dedup_users["pencilhead"] = "pencil-head"; | ||||
| $migrate_dedup_users["toml"] = "tomol"; | ||||
| $migrate_dedup_users["colinm"] = "tablaman"; | ||||
| $migrate_dedup_users["blendphys2"] = "blendphys"; | ||||
| $migrate_dedup_users["xgoff"] = "zzyxz"; | ||||
| $migrate_dedup_users["coleingraham"] = "coledingraham"; | ||||
| $migrate_dedup_users["danielvmacedo"] = "skul3r"; | ||||
| $migrate_dedup_users["burster"] = "przemaz"; | ||||
| $migrate_dedup_users["tung"] = "tungster"; | ||||
| $migrate_dedup_users["chessie"] = "blackcatt"; | ||||
| $migrate_dedup_users["foxdog"] = "rubbernuke"; | ||||
| $migrate_dedup_users["mordachai"] = "gus"; | ||||
| $migrate_dedup_users["chilamlai"] = "cllai"; | ||||
| $migrate_dedup_users["sliders34"] = "sliders"; | ||||
| $migrate_dedup_users["benji852"] = "benjamin852"; | ||||
| $migrate_dedup_users["bebinalpha"] = "bebin"; | ||||
| $migrate_dedup_users["mr78"] = "alexandr"; | ||||
| $migrate_dedup_users["avirillion"] = "tarion"; | ||||
| $migrate_dedup_users["matthiasro"] = "matthias_r"; | ||||
| $migrate_dedup_users["debearseax"] = "seogeniuss"; | ||||
| $migrate_dedup_users["karja"] = "trockenfrosch"; | ||||
| $migrate_dedup_users["rojuinex"] = "ifrit"; | ||||
| $migrate_dedup_users["bernardo"] = "dados"; | ||||
| $migrate_dedup_users["ddeclara"] = "decden"; | ||||
| $migrate_dedup_users["zm_sansan"] = "sansan"; | ||||
| $migrate_dedup_users["useless"] = "cortot"; | ||||
| $migrate_dedup_users["tymnclono"] = "sooccatly"; | ||||
| $migrate_dedup_users["rodrigo_b"] = "rodrigob"; | ||||
| $migrate_dedup_users["shnurui"] = "spinster"; | ||||
| $migrate_dedup_users["michalisz"] = "michaliszissiou"; | ||||
| $migrate_dedup_users["fbbdev"] = "babboide"; | ||||
| $migrate_dedup_users["tjackson"] = "tjonline"; | ||||
| $migrate_dedup_users["ramaswamy"] = "ramaswamysriram"; | ||||
| $migrate_dedup_users["allrod5"] = "rodblender"; | ||||
| $migrate_dedup_users["qcp"] = "qpblendpolis"; | ||||
| $migrate_dedup_users["ftsf"] = "thesleepless"; | ||||
| $migrate_dedup_users["umagoo2012"] = "umagoo"; | ||||
| $migrate_dedup_users["raven"] = "rune"; | ||||
| $migrate_dedup_users["hsaito"] = "integer"; | ||||
| $migrate_dedup_users["paulthegreat"] = "digitalpyro"; | ||||
| $migrate_dedup_users["capheen"] = "dval"; | ||||
| $migrate_dedup_users["rskinner"] = "rws"; | ||||
| $migrate_dedup_users["gregstein"] = "gregorein"; | ||||
| $migrate_dedup_users["matty686"] = "matty"; | ||||
| $migrate_dedup_users["selby_rowley"] = "selby"; | ||||
| $migrate_dedup_users["shembolstudio"] = "natadams8"; | ||||
| $migrate_dedup_users["grenzfrequence"] = "goatrance"; | ||||
| $migrate_dedup_users["stephan"] = "schdeffan"; | ||||
| $migrate_dedup_users["axis33"] = "dsc512"; | ||||
| $migrate_dedup_users["redandfish"] = "red-fish"; | ||||
| $migrate_dedup_users["artsapcemedia"] = "arzpace"; | ||||
| $migrate_dedup_users["artspacemedia"] = "arzpace"; | ||||
| $migrate_dedup_users["mccmcc"] = "mcq"; | ||||
| $migrate_dedup_users["seocitterx"] = "mediabuy"; | ||||
| $migrate_dedup_users["lightning_limn"] = "lightning4"; | ||||
| $migrate_dedup_users["omarlakhdar"] = "archimage"; | ||||
| $migrate_dedup_users["regeleionescu"] = "regele"; | ||||
| $migrate_dedup_users["mitchell_decker"] = "michealikruhara"; | ||||
| $migrate_dedup_users["joselebon"] = "jl57"; | ||||
| $migrate_dedup_users["simonbroggi"] = "broggsim"; | ||||
| $migrate_dedup_users["inwadnepe"] = "ceapbatatry"; | ||||
| $migrate_dedup_users["ehobjman"] = "resbsp"; | ||||
| $migrate_dedup_users["davelassanske"] = "dolby411"; | ||||
| $migrate_dedup_users["jsu"] = "jansub"; | ||||
| $migrate_dedup_users["agricola"] = "agricola1"; | ||||
| $migrate_dedup_users["bartoszek"] = "bartus"; | ||||
| $migrate_dedup_users["captainoblivion"] = "cptoblivion"; | ||||
| $migrate_dedup_users["alexmcourt"] = "personalex"; | ||||
| $migrate_dedup_users["jmsprss"] = "xonar"; | ||||
| $migrate_dedup_users["awarnock"] = "salsa"; | ||||
| $migrate_dedup_users["mcc2"] = "mcq"; | ||||
| $migrate_dedup_users["psyborg042"] = "psyborg"; | ||||
| $migrate_dedup_users["ushiproject"] = "ushi"; | ||||
| $migrate_dedup_users["mrjimmy"] = "mrjimmyos"; | ||||
| $migrate_dedup_users["thefinalcut"] = "tlousky"; | ||||
| $migrate_dedup_users["startheshadow"] = "star"; | ||||
| $migrate_dedup_users["axredneck"] = "redneck"; | ||||
| $migrate_dedup_users["phimestudio"] = "phime"; | ||||
| $migrate_dedup_users["dwatts1"] = "dlax"; | ||||
| $migrate_dedup_users["rertjoi"] = "rertjwi"; | ||||
| $migrate_dedup_users["erdjkgh"] = "rertjwi"; | ||||
| $migrate_dedup_users["libertainsrg"] = "fcougar"; | ||||
| $migrate_dedup_users["godling72"] = "dmelchio"; | ||||
| $migrate_dedup_users["myclay"] = "thenewone"; | ||||
| $migrate_dedup_users["ecaspersen"] = "ecasper"; | ||||
| $migrate_dedup_users["driewiel"] = "driesiedriewiel"; | ||||
| $migrate_dedup_users["bhupen"] = "bhupendra"; | ||||
| $migrate_dedup_users["caosdoar"] = "mailoyo"; | ||||
| $migrate_dedup_users["polyspin"] = "butler"; | ||||
| $migrate_dedup_users["qalb_al_aqrab"] = "efimpetelin"; | ||||
| $migrate_dedup_users["fdfdfdfffd"] = "fcougar"; | ||||
| $migrate_dedup_users["brianlockett"] = "macrow"; | ||||
| $migrate_dedup_users["claude"] = "coco"; | ||||
| $migrate_dedup_users["mattostgard"] = "drflail"; | ||||
| $migrate_dedup_users["cekuhnen_new"] = "cekuhnendev"; | ||||
| $migrate_dedup_users["kirill_lukyanov"] = "kirill"; | ||||
| $migrate_dedup_users["jan-eric"] = "janeric96"; | ||||
| $migrate_dedup_users["daniel_h"] = "dhoughto"; | ||||
| $migrate_dedup_users["raphaelbarros"] = "thebigheadone"; | ||||
| $migrate_dedup_users["salas"] = "tychota"; | ||||
| $migrate_dedup_users["danieljsamson"] = "techfix"; | ||||
| $migrate_dedup_users["vinagrito"] = "aechemendia"; | ||||
| $migrate_dedup_users["lin_165"] = "b1657022405"; | ||||
| $migrate_dedup_users["cwebber"] = "paroneayea"; | ||||
| $migrate_dedup_users["harolddadomo"] = "harold"; | ||||
| $migrate_dedup_users["rabidsquirrel"] = "genericusername"; | ||||
| $migrate_dedup_users["larry3"] = "lehibou"; | ||||
| $migrate_dedup_users["predoe"] = "petronius3d"; | ||||
| $migrate_dedup_users["skoo"] = "stefano"; | ||||
| $migrate_dedup_users["cabergolinety"] = "azathioprinewww"; | ||||
| $migrate_dedup_users["prestijkorsan07"] = "prestij07"; | ||||
| $migrate_dedup_users["scottpetrovic"] = "slpetrov"; | ||||
| $migrate_dedup_users["zooly76"] = "zooly"; | ||||
| $migrate_dedup_users["theoryanimation"] = "davidandrade"; | ||||
| $migrate_dedup_users["daninsky"] = "danishit"; | ||||
| $migrate_dedup_users["eyesee2013"] = "eyesee"; | ||||
| $migrate_dedup_users["megacal"] = "cmcgaugh"; | ||||
| $migrate_dedup_users["const"] = "kostas100"; | ||||
| $migrate_dedup_users["ngaudenzi"] = "puppetmaster"; | ||||
| $migrate_dedup_users["mroguski"] = "kaelthas"; | ||||
| $migrate_dedup_users["brdf"] = "origin"; | ||||
| $migrate_dedup_users["davis3d"] = "davis"; | ||||
| $migrate_dedup_users["rldigital"] = "locatelli"; | ||||
| $migrate_dedup_users["tomforsythe"] = "gallifrey77203"; | ||||
| $migrate_dedup_users["gbrnk"] = "benoe"; | ||||
| $migrate_dedup_users["arekkasprzyk"] = "kasperski"; | ||||
| $migrate_dedup_users["imbusy1"] = "imbusy"; | ||||
| $migrate_dedup_users["mfoxdoggg"] = "mfoxdogg"; | ||||
|  | ||||
| $migrate_dedup_users["knusk"] = "kanutus"; | ||||
| $migrate_dedup_users["tomekk"] = "anders211"; | ||||
| $migrate_dedup_users["kitsueb"] = "kitsu_eb"; | ||||
| $migrate_dedup_users["slugzzz"] = "tsquar3d"; | ||||
| $migrate_dedup_users["moore500"] = "mmoore500"; | ||||
| $migrate_dedup_users["verumbra"] = "sebastian0"; | ||||
| $migrate_dedup_users["blenderbug"] = "nikola"; | ||||
|  | ||||
| $migrate_dedup_users["adailtoncomp"] = "adailton"; | ||||
| $migrate_dedup_users["mchs3d"] = "abtrumpet"; | ||||
|  | ||||
| // disabled users who have tasks | ||||
| $migrate_dedup_users["sjoerd"] = "sjoerddevries"; | ||||
| $migrate_dedup_users["matali"] = "mat_ali"; | ||||
| $migrate_dedup_users["voicelesscushio"] = "None"; | ||||
| $migrate_dedup_users["bigben0328"] = "None"; | ||||
| $migrate_dedup_users["santamouse"] = "None"; | ||||
| $migrate_dedup_users["andreanckaert"] = "None"; | ||||
| $migrate_dedup_users["yesmydear"] = "None"; | ||||
| $migrate_dedup_users["spacetug"] = "None"; | ||||
| $migrate_dedup_users["omegafold"] = "None"; | ||||
|  | ||||
| // testing | ||||
| $migrate_dedup_users["blendix_rename_test_a"] = "blendix"; | ||||
| $migrate_dedup_users["blendix_rename_test_b"] = "blendix"; | ||||
|  | ||||
| Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.8 KiB | 
| Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 13 KiB | 
| Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.4 KiB | 
| @@ -54,6 +54,8 @@ return array( | ||||
|     'javelin-behavior-aphlict-dropdown', | ||||
|     'javelin-behavior-history-install', | ||||
|     'javelin-behavior-phabricator-gesture', | ||||
|     'javelin-behavior-phabricator-active-nav', | ||||
|     'javelin-behavior-phabricator-nav', | ||||
|     'javelin-behavior-phabricator-remarkup-assist', | ||||
|     'phabricator-textareautils', | ||||
|     'phabricator-file-upload', | ||||
| @@ -86,12 +88,6 @@ return array( | ||||
|     'javelin-behavior-aphlict-status', | ||||
|     'javelin-behavior-user-menu', | ||||
|     'phabricator-favicon', | ||||
|     'javelin-behavior-phui-tab-group', | ||||
|     'javelin-behavior-phui-submenu', | ||||
|     'phuix-button-view', | ||||
|     'javelin-behavior-comment-actions', | ||||
|     'phuix-form-control-view', | ||||
|     'phuix-autocomplete', | ||||
|   ), | ||||
|   'core.pkg.css' => array( | ||||
|     'phabricator-core-css', | ||||
| @@ -162,14 +158,9 @@ return array( | ||||
|     'phabricator-feed-css', | ||||
|     'phabricator-dashboard-css', | ||||
|     'aphront-multi-column-view-css', | ||||
|     'phui-curtain-object-ref-view-css', | ||||
|     'phui-comment-form-css', | ||||
|     'phui-head-thing-view-css', | ||||
|  | ||||
|     'conpherence-durable-column-view', | ||||
|     'phui-button-bar-css', | ||||
|   ), | ||||
|   'conpherence.pkg.css' => array( | ||||
|     'conpherence-durable-column-view', | ||||
|     'conpherence-menu-css', | ||||
|     'conpherence-color-css', | ||||
|     'conpherence-message-pane-css', | ||||
| @@ -196,8 +187,7 @@ return array( | ||||
|     'phabricator-content-source-view-css', | ||||
|     'inline-comment-summary-css', | ||||
|     'phui-inline-comment-view-css', | ||||
|     'diff-tree-view-css', | ||||
|     'phui-formation-view-css', | ||||
|     'phabricator-filetree-view-css', | ||||
|   ), | ||||
|   'differential.pkg.js' => array( | ||||
|     'phabricator-drag-and-drop-file-upload', | ||||
| @@ -214,14 +204,6 @@ return array( | ||||
|     'phabricator-diff-inline', | ||||
|     'phabricator-diff-changeset', | ||||
|     'phabricator-diff-changeset-list', | ||||
|     'phabricator-diff-tree-view', | ||||
|     'phabricator-diff-path-view', | ||||
|  | ||||
|     'phuix-formation-view', | ||||
|     'phuix-formation-column-view', | ||||
|     'phuix-formation-flank-view', | ||||
|  | ||||
|     'javelin-external-editor-link-engine', | ||||
|   ), | ||||
|   'diffusion.pkg.css' => array( | ||||
|     'diffusion-icons-css', | ||||
| @@ -238,9 +220,4 @@ return array( | ||||
|     'javelin-behavior-maniphest-batch-selector', | ||||
|     'javelin-behavior-maniphest-list-editor', | ||||
|   ), | ||||
|   'dark-console.pkg.js' => array( | ||||
|     'javelin-behavior-dark-console', | ||||
|     'phabricator-darklog', | ||||
|     'phabricator-darkmessage', | ||||
|   ), | ||||
| ); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ $status_map = array( | ||||
|   3 => 'invalid', | ||||
|   4 => 'duplicate', | ||||
|   5 => 'spite', | ||||
|   123450 => 'archived', | ||||
| ); | ||||
|  | ||||
| $conn_w = id(new ManiphestTask())->establishConnection('w'); | ||||
|   | ||||
| @@ -16,9 +16,6 @@ foreach (new LiskMigrationIterator($table) as $doc) { | ||||
|     continue; | ||||
|   } | ||||
|  | ||||
|   $new_view_policy = $default_view_policy; | ||||
|   $new_edit_policy = $default_edit_policy; | ||||
|  | ||||
|   // If this was previously a magical project wiki page (under projects/, but | ||||
|   // not projects/ itself) we need to apply the project policies. Otherwise, | ||||
|   // apply the default policies. | ||||
| @@ -38,24 +35,26 @@ foreach (new LiskMigrationIterator($table) as $doc) { | ||||
|       ->executeOne(); | ||||
|  | ||||
|     if ($project) { | ||||
|  | ||||
|       $view_policy = nonempty($project->getViewPolicy(), $default_view_policy); | ||||
|       $edit_policy = nonempty($project->getEditPolicy(), $default_edit_policy); | ||||
|  | ||||
|       $new_view_policy = $view_policy; | ||||
|       $new_edit_policy = $edit_policy; | ||||
|       $project_name = $project->getName(); | ||||
|       echo pht( | ||||
|         "Migrating document %d to project policy %s...\n", | ||||
|         $id, | ||||
|         $project_name); | ||||
|       $doc->setViewPolicy($view_policy); | ||||
|       $doc->setEditPolicy($edit_policy); | ||||
|       $doc->save(); | ||||
|       continue; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   echo pht('Migrating document %d to new policy...', $id)."\n"; | ||||
|  | ||||
|   queryfx( | ||||
|     $conn_w, | ||||
|     'UPDATE %R SET viewPolicy = %s, editPolicy = %s | ||||
|       WHERE id = %d', | ||||
|     $table, | ||||
|     $new_view_policy, | ||||
|     $new_edit_policy, | ||||
|     $id); | ||||
|   echo pht('Migrating document %d to default install policy...', $id)."\n"; | ||||
|   $doc->setViewPolicy($default_view_policy); | ||||
|   $doc->setEditPolicy($default_edit_policy); | ||||
|   $doc->save(); | ||||
| } | ||||
|  | ||||
| echo pht('Done.')."\n"; | ||||
|   | ||||
| @@ -40,8 +40,7 @@ foreach ($lists as $list) { | ||||
|   if (!$username_okay) { | ||||
|     echo pht( | ||||
|       'Failed to migrate mailing list "%s": unable to generate a unique '. | ||||
|       'username for it.', | ||||
|       $name)."\n"; | ||||
|       'username for it.')."\n"; | ||||
|     continue; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -4,8 +4,18 @@ | ||||
| // underlying file data were not written correctly. This restores edges for | ||||
| // any missing pastes. | ||||
|  | ||||
| // See T13510. The "pastebin" database was later renamed to "paste", which | ||||
| // broke this migration. The migration was removed in 2020 since it seems | ||||
| // plausible that zero installs are impacted (only installs that ran code | ||||
| // from November 2015 and have not upgraded in five years could possibly be | ||||
| // impacted). | ||||
| $table = new PhabricatorPaste(); | ||||
| $edge_type = PhabricatorObjectHasFileEdgeType::EDGECONST; | ||||
|  | ||||
| foreach (new LiskMigrationIterator($table) as $paste) { | ||||
|   $paste_phid = $paste->getPHID(); | ||||
|   $file_phid = $paste->getFilePHID(); | ||||
|  | ||||
|   if (!$file_phid) { | ||||
|     continue; | ||||
|   } | ||||
|  | ||||
|   id(new PhabricatorEdgeEditor()) | ||||
|     ->addEdge($paste_phid, $edge_type, $file_phid) | ||||
|     ->save(); | ||||
| } | ||||
|   | ||||
| @@ -1,10 +0,0 @@ | ||||
| CREATE TABLE {$NAMESPACE}_user.user_externalaccountidentifier ( | ||||
|   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||
|   phid VARBINARY(64) NOT NULL, | ||||
|   externalAccountPHID VARBINARY(64) NOT NULL, | ||||
|   providerConfigPHID VARBINARY(64) NOT NULL, | ||||
|   identifierHash BINARY(12) NOT NULL, | ||||
|   identifierRaw LONGTEXT NOT NULL, | ||||
|   dateCreated INT UNSIGNED NOT NULL, | ||||
|   dateModified INT UNSIGNED NOT NULL | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,40 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| $account_table = new PhabricatorExternalAccount(); | ||||
| $identifier_table = new PhabricatorExternalAccountIdentifier(); | ||||
|  | ||||
| $conn = $account_table->establishConnection('w'); | ||||
| $table_name = $account_table->getTableName(); | ||||
|  | ||||
| $iterator = new LiskRawMigrationIterator($conn, $table_name); | ||||
| foreach ($iterator as $account_row) { | ||||
|   // We don't need to migrate "accountID" values for "password" accounts, | ||||
|   // since these were dummy values in the first place and are no longer | ||||
|   // read or written after D21014. (There would be no harm in writing these | ||||
|   // rows, but it's easy to skip them.) | ||||
|  | ||||
|   if ($account_row['accountType'] === 'password') { | ||||
|     continue; | ||||
|   } | ||||
|  | ||||
|   $account_id = $account_row['accountID']; | ||||
|   if (!strlen($account_id)) { | ||||
|     continue; | ||||
|   } | ||||
|  | ||||
|   queryfx( | ||||
|     $conn, | ||||
|     'INSERT IGNORE INTO %R ( | ||||
|         phid, externalAccountPHID, providerConfigPHID, | ||||
|         identifierHash, identifierRaw, | ||||
|         dateCreated, dateModified) | ||||
|       VALUES (%s, %s, %s, %s, %s, %d, %d)', | ||||
|     $identifier_table, | ||||
|     $identifier_table->generatePHID(), | ||||
|     $account_row['phid'], | ||||
|     $account_row['providerConfigPHID'], | ||||
|     PhabricatorHash::digestForIndex($account_id), | ||||
|     $account_id, | ||||
|     $account_row['dateCreated'], | ||||
|     $account_row['dateModified']); | ||||
| } | ||||
| @@ -1,21 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| // See T13493. This table previously had a UNIQUE KEY on "<accountType, | ||||
| // accountDomain, accountID>", which is obsolete. The application now violates | ||||
| // this key, so make sure it gets dropped. | ||||
|  | ||||
| // There's no "IF EXISTS" modifier for "ALTER TABLE" so run this as a PHP patch | ||||
| // instead of an SQL patch. | ||||
|  | ||||
| $table = new PhabricatorExternalAccount(); | ||||
| $conn = $table->establishConnection('w'); | ||||
|  | ||||
| try { | ||||
|   queryfx( | ||||
|     $conn, | ||||
|     'ALTER TABLE %R DROP KEY %T', | ||||
|     $table, | ||||
|     'account_details'); | ||||
| } catch (AphrontQueryException $ex) { | ||||
|   // Ignore. | ||||
| } | ||||
| @@ -1,9 +0,0 @@ | ||||
| CREATE TABLE {$NAMESPACE}_paste.paste_paste_fdocument ( | ||||
|   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||
|   objectPHID VARBINARY(64) NOT NULL, | ||||
|   isClosed BOOL NOT NULL, | ||||
|   authorPHID VARBINARY(64), | ||||
|   ownerPHID VARBINARY(64), | ||||
|   epochCreated INT UNSIGNED NOT NULL, | ||||
|   epochModified INT UNSIGNED NOT NULL | ||||
| ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,8 +0,0 @@ | ||||
| CREATE TABLE {$NAMESPACE}_paste.paste_paste_ffield ( | ||||
|   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||
|   documentID INT UNSIGNED NOT NULL, | ||||
|   fieldKey VARCHAR(4) NOT NULL COLLATE {$COLLATE_TEXT}, | ||||
|   rawCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}, | ||||
|   termCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT}, | ||||
|   normalCorpus LONGTEXT NOT NULL COLLATE {$COLLATE_SORT} | ||||
| ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,5 +0,0 @@ | ||||
| CREATE TABLE {$NAMESPACE}_paste.paste_paste_fngrams ( | ||||
|   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||
|   documentID INT UNSIGNED NOT NULL, | ||||
|   ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT} | ||||
| ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,7 +0,0 @@ | ||||
| CREATE TABLE {$NAMESPACE}_paste.paste_paste_fngrams_common ( | ||||
|   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||
|   ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT}, | ||||
|   needsCollection BOOL NOT NULL, | ||||
|   UNIQUE KEY `key_ngram` (ngram), | ||||
|   KEY `key_collect` (needsCollection) | ||||
| ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,9 +0,0 @@ | ||||
| CREATE TABLE {$NAMESPACE}_differential.differential_viewstate ( | ||||
|   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||
|   viewerPHID VARBINARY(64) NOT NULL, | ||||
|   objectPHID VARBINARY(64) NOT NULL, | ||||
|   viewState LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, | ||||
|   dateCreated INT UNSIGNED NOT NULL, | ||||
|   dateModified INT UNSIGNED NOT NULL, | ||||
|   UNIQUE KEY `key_viewer` (viewerPHID, objectPHID) | ||||
| ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,2 +0,0 @@ | ||||
| ALTER TABLE {$NAMESPACE}_differential.differential_transaction_comment | ||||
|   ADD attributes LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,2 +0,0 @@ | ||||
| ALTER TABLE {$NAMESPACE}_audit.audit_transaction_comment | ||||
|   ADD attributes LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1,2 +0,0 @@ | ||||
| UPDATE {$NAMESPACE}_differential.differential_transaction_comment | ||||
|   SET attributes = '{}' WHERE attributes = ''; | ||||
| @@ -1,2 +0,0 @@ | ||||
| UPDATE {$NAMESPACE}_audit.audit_transaction_comment | ||||
|   SET attributes = '{}' WHERE attributes = ''; | ||||
| @@ -1 +0,0 @@ | ||||
| DROP TABLE {$NAMESPACE}_differential.differential_changeset_parse_cache; | ||||
| @@ -1,7 +0,0 @@ | ||||
| CREATE TABLE {$NAMESPACE}_differential.differential_changeset_parse_cache ( | ||||
|   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||||
|   cacheIndex BINARY(12) NOT NULL, | ||||
|   cache LONGBLOB NOT NULL, | ||||
|   dateCreated INT UNSIGNED NOT NULL, | ||||
|   UNIQUE KEY `key_cacheIndex` (cacheIndex) | ||||
| ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; | ||||
| @@ -1 +0,0 @@ | ||||
| DROP TABLE IF EXISTS {$NAMESPACE}_differential.differential_commit; | ||||
| @@ -1,46 +0,0 @@ | ||||
| #!/usr/bin/env php | ||||
| <?php | ||||
|  | ||||
| $root = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $root.'/scripts/init/init-script.php'; | ||||
|  | ||||
| $xml = $root.'/externals/cldr/cldr_windows_timezones.xml'; | ||||
| $xml = Filesystem::readFile($xml); | ||||
| $xml = new SimpleXMLElement($xml); | ||||
|  | ||||
| $result_map = array(); | ||||
|  | ||||
| $ignore = array( | ||||
|   'UTC', | ||||
|   'UTC-11', | ||||
|   'UTC-02', | ||||
|   'UTC-08', | ||||
|   'UTC-09', | ||||
|   'UTC+12', | ||||
| ); | ||||
| $ignore = array_fuse($ignore); | ||||
|  | ||||
| $zones = $xml->windowsZones->mapTimezones->mapZone; | ||||
| foreach ($zones as $zone) { | ||||
|   $windows_name = (string)$zone['other']; | ||||
|   $target_name = (string)$zone['type']; | ||||
|  | ||||
|   // Ignore the offset-based timezones from the CLDR map, since we handle | ||||
|   // these later. | ||||
|   if (isset($ignore[$windows_name])) { | ||||
|     continue; | ||||
|   } | ||||
|  | ||||
|   // We've already seen this timezone so we don't need to add it to the map | ||||
|   // again. | ||||
|   if (isset($result_map[$windows_name])) { | ||||
|     continue; | ||||
|   } | ||||
|  | ||||
|   $result_map[$windows_name] = $target_name; | ||||
| } | ||||
|  | ||||
| asort($result_map); | ||||
|  | ||||
| echo id(new PhutilJSON()) | ||||
|   ->encodeFormatted($result_map); | ||||
| @@ -1,126 +0,0 @@ | ||||
| { | ||||
|   "Egypt Standard Time": "Africa/Cairo", | ||||
|   "Morocco Standard Time": "Africa/Casablanca", | ||||
|   "South Africa Standard Time": "Africa/Johannesburg", | ||||
|   "W. Central Africa Standard Time": "Africa/Lagos", | ||||
|   "E. Africa Standard Time": "Africa/Nairobi", | ||||
|   "Libya Standard Time": "Africa/Tripoli", | ||||
|   "Namibia Standard Time": "Africa/Windhoek", | ||||
|   "Aleutian Standard Time": "America/Adak", | ||||
|   "Alaskan Standard Time": "America/Anchorage", | ||||
|   "Tocantins Standard Time": "America/Araguaina", | ||||
|   "Paraguay Standard Time": "America/Asuncion", | ||||
|   "Bahia Standard Time": "America/Bahia", | ||||
|   "SA Pacific Standard Time": "America/Bogota", | ||||
|   "Argentina Standard Time": "America/Buenos_Aires", | ||||
|   "Eastern Standard Time (Mexico)": "America/Cancun", | ||||
|   "Venezuela Standard Time": "America/Caracas", | ||||
|   "SA Eastern Standard Time": "America/Cayenne", | ||||
|   "Central Standard Time": "America/Chicago", | ||||
|   "Mountain Standard Time (Mexico)": "America/Chihuahua", | ||||
|   "Central Brazilian Standard Time": "America/Cuiaba", | ||||
|   "Mountain Standard Time": "America/Denver", | ||||
|   "Greenland Standard Time": "America/Godthab", | ||||
|   "Turks And Caicos Standard Time": "America/Grand_Turk", | ||||
|   "Central America Standard Time": "America/Guatemala", | ||||
|   "Atlantic Standard Time": "America/Halifax", | ||||
|   "Cuba Standard Time": "America/Havana", | ||||
|   "US Eastern Standard Time": "America/Indianapolis", | ||||
|   "SA Western Standard Time": "America/La_Paz", | ||||
|   "Pacific Standard Time": "America/Los_Angeles", | ||||
|   "Central Standard Time (Mexico)": "America/Mexico_City", | ||||
|   "Saint Pierre Standard Time": "America/Miquelon", | ||||
|   "Montevideo Standard Time": "America/Montevideo", | ||||
|   "Eastern Standard Time": "America/New_York", | ||||
|   "US Mountain Standard Time": "America/Phoenix", | ||||
|   "Haiti Standard Time": "America/Port-au-Prince", | ||||
|   "Canada Central Standard Time": "America/Regina", | ||||
|   "Pacific SA Standard Time": "America/Santiago", | ||||
|   "E. South America Standard Time": "America/Sao_Paulo", | ||||
|   "Newfoundland Standard Time": "America/St_Johns", | ||||
|   "Pacific Standard Time (Mexico)": "America/Tijuana", | ||||
|   "Central Asia Standard Time": "Asia/Almaty", | ||||
|   "Jordan Standard Time": "Asia/Amman", | ||||
|   "Arabic Standard Time": "Asia/Baghdad", | ||||
|   "Azerbaijan Standard Time": "Asia/Baku", | ||||
|   "SE Asia Standard Time": "Asia/Bangkok", | ||||
|   "Altai Standard Time": "Asia/Barnaul", | ||||
|   "Middle East Standard Time": "Asia/Beirut", | ||||
|   "India Standard Time": "Asia/Calcutta", | ||||
|   "Transbaikal Standard Time": "Asia/Chita", | ||||
|   "Sri Lanka Standard Time": "Asia/Colombo", | ||||
|   "Syria Standard Time": "Asia/Damascus", | ||||
|   "Bangladesh Standard Time": "Asia/Dhaka", | ||||
|   "Arabian Standard Time": "Asia/Dubai", | ||||
|   "West Bank Standard Time": "Asia/Hebron", | ||||
|   "W. Mongolia Standard Time": "Asia/Hovd", | ||||
|   "North Asia East Standard Time": "Asia/Irkutsk", | ||||
|   "Israel Standard Time": "Asia/Jerusalem", | ||||
|   "Afghanistan Standard Time": "Asia/Kabul", | ||||
|   "Russia Time Zone 11": "Asia/Kamchatka", | ||||
|   "Pakistan Standard Time": "Asia/Karachi", | ||||
|   "Nepal Standard Time": "Asia/Katmandu", | ||||
|   "North Asia Standard Time": "Asia/Krasnoyarsk", | ||||
|   "Magadan Standard Time": "Asia/Magadan", | ||||
|   "N. Central Asia Standard Time": "Asia/Novosibirsk", | ||||
|   "Omsk Standard Time": "Asia/Omsk", | ||||
|   "North Korea Standard Time": "Asia/Pyongyang", | ||||
|   "Myanmar Standard Time": "Asia/Rangoon", | ||||
|   "Arab Standard Time": "Asia/Riyadh", | ||||
|   "Sakhalin Standard Time": "Asia/Sakhalin", | ||||
|   "Korea Standard Time": "Asia/Seoul", | ||||
|   "China Standard Time": "Asia/Shanghai", | ||||
|   "Singapore Standard Time": "Asia/Singapore", | ||||
|   "Russia Time Zone 10": "Asia/Srednekolymsk", | ||||
|   "Taipei Standard Time": "Asia/Taipei", | ||||
|   "West Asia Standard Time": "Asia/Tashkent", | ||||
|   "Georgian Standard Time": "Asia/Tbilisi", | ||||
|   "Iran Standard Time": "Asia/Tehran", | ||||
|   "Tokyo Standard Time": "Asia/Tokyo", | ||||
|   "Tomsk Standard Time": "Asia/Tomsk", | ||||
|   "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", | ||||
|   "Vladivostok Standard Time": "Asia/Vladivostok", | ||||
|   "Yakutsk Standard Time": "Asia/Yakutsk", | ||||
|   "Ekaterinburg Standard Time": "Asia/Yekaterinburg", | ||||
|   "Caucasus Standard Time": "Asia/Yerevan", | ||||
|   "Azores Standard Time": "Atlantic/Azores", | ||||
|   "Cape Verde Standard Time": "Atlantic/Cape_Verde", | ||||
|   "Greenwich Standard Time": "Atlantic/Reykjavik", | ||||
|   "Cen. Australia Standard Time": "Australia/Adelaide", | ||||
|   "E. Australia Standard Time": "Australia/Brisbane", | ||||
|   "AUS Central Standard Time": "Australia/Darwin", | ||||
|   "Aus Central W. Standard Time": "Australia/Eucla", | ||||
|   "Tasmania Standard Time": "Australia/Hobart", | ||||
|   "Lord Howe Standard Time": "Australia/Lord_Howe", | ||||
|   "W. Australia Standard Time": "Australia/Perth", | ||||
|   "AUS Eastern Standard Time": "Australia/Sydney", | ||||
|   "Dateline Standard Time": "Etc/GMT+12", | ||||
|   "Astrakhan Standard Time": "Europe/Astrakhan", | ||||
|   "W. Europe Standard Time": "Europe/Berlin", | ||||
|   "GTB Standard Time": "Europe/Bucharest", | ||||
|   "Central Europe Standard Time": "Europe/Budapest", | ||||
|   "E. Europe Standard Time": "Europe/Chisinau", | ||||
|   "Turkey Standard Time": "Europe/Istanbul", | ||||
|   "Kaliningrad Standard Time": "Europe/Kaliningrad", | ||||
|   "FLE Standard Time": "Europe/Kiev", | ||||
|   "GMT Standard Time": "Europe/London", | ||||
|   "Belarus Standard Time": "Europe/Minsk", | ||||
|   "Russian Standard Time": "Europe/Moscow", | ||||
|   "Romance Standard Time": "Europe/Paris", | ||||
|   "Russia Time Zone 3": "Europe/Samara", | ||||
|   "Central European Standard Time": "Europe/Warsaw", | ||||
|   "Mauritius Standard Time": "Indian/Mauritius", | ||||
|   "Samoa Standard Time": "Pacific/Apia", | ||||
|   "New Zealand Standard Time": "Pacific/Auckland", | ||||
|   "Bougainville Standard Time": "Pacific/Bougainville", | ||||
|   "Chatham Islands Standard Time": "Pacific/Chatham", | ||||
|   "Easter Island Standard Time": "Pacific/Easter", | ||||
|   "Fiji Standard Time": "Pacific/Fiji", | ||||
|   "Central Pacific Standard Time": "Pacific/Guadalcanal", | ||||
|   "Hawaiian Standard Time": "Pacific/Honolulu", | ||||
|   "Line Islands Standard Time": "Pacific/Kiritimati", | ||||
|   "Marquesas Standard Time": "Pacific/Marquesas", | ||||
|   "Norfolk Standard Time": "Pacific/Norfolk", | ||||
|   "West Pacific Standard Time": "Pacific/Port_Moresby", | ||||
|   "Tonga Standard Time": "Pacific/Tongatapu" | ||||
| } | ||||
							
								
								
									
										172
									
								
								scripts/auth_provider/auth_provider.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,172 @@ | ||||
| #!/usr/local/bin/php | ||||
| <?php | ||||
| # | ||||
| # ***** BEGIN GPL LICENSE BLOCK ***** | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or | ||||
| # modify it under the terms of the GNU General Public License | ||||
| # as published by the Free Software Foundation; either version 2 | ||||
| # of the License, or (at your option) any later version. | ||||
| # | ||||
| # This program 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 General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software Foundation, | ||||
| # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| # | ||||
| # The Original Code is Copyright (C) 2019, Blender Foundation | ||||
| # All rights reserved. | ||||
| # | ||||
| # Contributor(s): Sergey Sharybin. | ||||
| # | ||||
| # ***** END GPL LICENSE BLOCK ***** | ||||
| # | ||||
| # This scripts implements external AuthBasicProvider which can be used | ||||
| # to authentificate users using Phabricator's database. | ||||
| # | ||||
| # Example configuration: | ||||
| # | ||||
| # .htaccess file: | ||||
| # | ||||
| #   AuthType Basic | ||||
| #   AuthName "Please Enter Password" | ||||
| #   AuthBasicProvider external | ||||
| #   AuthExternal phabricator | ||||
| #   Require valid-user | ||||
| # | ||||
| # It is also required to have the following provider registered in the | ||||
| # configuration. There are two ways to do it: | ||||
| # | ||||
| #  1. Separate conf file in /etc/apache2/conf-enabled | ||||
| #     Create a file (say, authz_external_phabricator.conf) with the following | ||||
| #     content: | ||||
| # | ||||
| #       DefineExternalAuth phabricator pipe /path/to/auth_provider.php | ||||
| # | ||||
| #     This method allows to use provider in any VHOST. | ||||
| # | ||||
| #     Downside: from local tests .htaccess file picks the provider nicely, | ||||
| #     but attempts to specifu the provider in virtual host configuration | ||||
| #     resulted in issues (seems to be different initialization order between | ||||
| #     global config in those files and virtual hosts). | ||||
| # | ||||
| #  2. Define provider in the virtual host definition: | ||||
| # | ||||
| #       <IfModule mod_authnz_external.c> | ||||
| #         AddExternalAuth phabricator /path/to/auth_provider.php | ||||
| #         SetExternalAuthMethod phabricator pipe | ||||
| #       </IfModule> | ||||
| # | ||||
| #     This method allowed to use this auth provider for SVN DAV. | ||||
|  | ||||
| $IS_DEBUG = false; | ||||
| $PHABRICATOR_ROOT = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $PHABRICATOR_ROOT.'/scripts/__init_script__.php'; | ||||
|  | ||||
| function initLogging() { | ||||
|   global $IS_DEBUG; | ||||
|   global $argv; | ||||
|   for ($i = 1; $i < count($argv); ++$i) { | ||||
|     if ($argv[$i] == '--debug') { | ||||
|       $IS_DEBUG = true; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| function debugLog(string $text) { | ||||
|   global $IS_DEBUG; | ||||
|   if (!$IS_DEBUG) { | ||||
|     return; | ||||
|   } | ||||
|   print("${text}\n"); | ||||
| } | ||||
|  | ||||
| function removeSingleTrailingNewline(string $string) { | ||||
|   if ($string === '') { | ||||
|     return $string; | ||||
|   } | ||||
|   $length = strlen($string); | ||||
|   if ($string[$length - 1] == "\n") { | ||||
|     return substr($string, 0, -1); | ||||
|   } | ||||
|   return $string; | ||||
| } | ||||
|  | ||||
| class AuthRequest { | ||||
|   public $user_name; | ||||
|   public $password; | ||||
|  | ||||
|   static function fromStdin() { | ||||
|     $auth_request = new AuthRequest(); | ||||
|     $stdin = fopen('php://stdin', 'r'); | ||||
|     $auth_request->user_name = removeSingleTrailingNewline(fgets($stdin)); | ||||
|     $auth_request->password = removeSingleTrailingNewline(fgets($stdin)); | ||||
|     return $auth_request; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| function getUserForAuthRequest(AuthRequest $auth_request) { | ||||
|   if ($auth_request->user_name === '') { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   $username_or_email = $auth_request->user_name; | ||||
|  | ||||
|   $user = id(new PhabricatorUser())->loadOneWhere( | ||||
|     'username = %s', | ||||
|     $username_or_email); | ||||
|  | ||||
|   if (!$user) { | ||||
|     $user = PhabricatorUser::loadOneWithEmailAddress( | ||||
|     $username_or_email); | ||||
|   } | ||||
|  | ||||
|   return $user; | ||||
| } | ||||
|  | ||||
| function createContentSourceForAuth() { | ||||
|   return PhabricatorContentSource::newForSource( | ||||
|       PhabricatorUnitTestContentSource::SOURCECONST); | ||||
| } | ||||
|  | ||||
| function isValidAuth(AuthRequest $auth_request) { | ||||
|   debugLog("Begin authentification check for user '$auth_request->user_name'"); | ||||
|   $user = getUserForAuthRequest($auth_request); | ||||
|   if (!$user) { | ||||
|     debugLog("No PhabricatorUser() object found for requested user."); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   $content_source = createContentSourceForAuth(); | ||||
|   $envelope = new PhutilOpaqueEnvelope($auth_request->password); | ||||
|  | ||||
|   $engine = id(new PhabricatorAuthPasswordEngine()) | ||||
|       ->setViewer($user) | ||||
|       ->setContentSource($content_source) | ||||
|       ->setPasswordType(PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT) | ||||
|       ->setObject($user); | ||||
|  | ||||
|   if (!$engine->isValidPassword($envelope)) { | ||||
|     debugLog('Engine reported invalid password.'); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   debugLog('Authentirfication passed.'); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| function main() { | ||||
|   initLogging(); | ||||
|   $auth_request = AuthRequest::fromStdin(); | ||||
|   if (!isValidAuth($auth_request)) { | ||||
|     exit(1); | ||||
|   } | ||||
|   exit(0); | ||||
| } | ||||
|  | ||||
| main(); | ||||
| ?> | ||||
| @@ -1,131 +0,0 @@ | ||||
| #!/usr/bin/env php | ||||
| <?php | ||||
|  | ||||
| if (function_exists('pcntl_async_signals')) { | ||||
|   pcntl_async_signals(true); | ||||
| } else { | ||||
|   declare(ticks = 1); | ||||
| } | ||||
|  | ||||
| require_once dirname(__FILE__).'/../../__init_script__.php'; | ||||
|  | ||||
| if (!posix_isatty(STDOUT)) { | ||||
|   $sid = posix_setsid(); | ||||
|   if ($sid <= 0) { | ||||
|     throw new Exception(pht('Failed to create new process session!')); | ||||
|   } | ||||
| } | ||||
|  | ||||
| $args = new PhutilArgumentParser($argv); | ||||
| $args->setTagline(pht('daemon executor')); | ||||
| $args->setSynopsis(<<<EOHELP | ||||
| **exec_daemon.php** [__options__] __daemon__ ... | ||||
|     Run an instance of __daemon__. | ||||
| EOHELP | ||||
|   ); | ||||
| $args->parse( | ||||
|   array( | ||||
|     array( | ||||
|       'name' => 'trace', | ||||
|       'help' => pht('Enable debug tracing.'), | ||||
|     ), | ||||
|     array( | ||||
|       'name' => 'trace-memory', | ||||
|       'help' => pht('Enable debug memory tracing.'), | ||||
|     ), | ||||
|     array( | ||||
|       'name' => 'verbose', | ||||
|       'help'  => pht('Enable verbose activity logging.'), | ||||
|     ), | ||||
|     array( | ||||
|       'name' => 'label', | ||||
|       'short' => 'l', | ||||
|       'param' => 'label', | ||||
|       'help' => pht( | ||||
|         'Optional process label. Makes "%s" nicer, no behavioral effects.', | ||||
|         'ps'), | ||||
|     ), | ||||
|     array( | ||||
|       'name'     => 'daemon', | ||||
|       'wildcard' => true, | ||||
|     ), | ||||
|   )); | ||||
|  | ||||
| $trace_memory = $args->getArg('trace-memory'); | ||||
| $trace_mode = $args->getArg('trace') || $trace_memory; | ||||
| $verbose = $args->getArg('verbose'); | ||||
|  | ||||
| if (function_exists('posix_isatty') && posix_isatty(STDIN)) { | ||||
|   fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n"); | ||||
| } | ||||
| $config = @file_get_contents('php://stdin'); | ||||
| $config = id(new PhutilJSONParser())->parse($config); | ||||
|  | ||||
| PhutilTypeSpec::checkMap( | ||||
|   $config, | ||||
|   array( | ||||
|     'log' => 'optional string|null', | ||||
|     'argv' => 'optional list<wild>', | ||||
|     'load' => 'optional list<string>', | ||||
|     'down' => 'optional int', | ||||
|   )); | ||||
|  | ||||
| $log = idx($config, 'log'); | ||||
|  | ||||
| if ($log) { | ||||
|   ini_set('error_log', $log); | ||||
|   PhutilErrorHandler::setErrorListener(array('PhutilDaemon', 'errorListener')); | ||||
| } | ||||
|  | ||||
| $load = idx($config, 'load', array()); | ||||
| foreach ($load as $library) { | ||||
|   $library = Filesystem::resolvePath($library); | ||||
|   phutil_load_library($library); | ||||
| } | ||||
|  | ||||
| PhutilErrorHandler::initialize(); | ||||
|  | ||||
| $daemon = $args->getArg('daemon'); | ||||
| if (!$daemon) { | ||||
|   throw new PhutilArgumentUsageException( | ||||
|     pht('Specify which class of daemon to start.')); | ||||
| } else if (count($daemon) > 1) { | ||||
|   throw new PhutilArgumentUsageException( | ||||
|     pht('Specify exactly one daemon to start.')); | ||||
| } else { | ||||
|   $daemon = head($daemon); | ||||
|   if (!class_exists($daemon)) { | ||||
|     throw new PhutilArgumentUsageException( | ||||
|       pht( | ||||
|         'No class "%s" exists in any known library.', | ||||
|         $daemon)); | ||||
|   } else if (!is_subclass_of($daemon, 'PhutilDaemon')) { | ||||
|     throw new PhutilArgumentUsageException( | ||||
|       pht( | ||||
|         'Class "%s" is not a subclass of "%s".', | ||||
|         $daemon, | ||||
|         'PhutilDaemon')); | ||||
|   } | ||||
| } | ||||
|  | ||||
| $argv = idx($config, 'argv', array()); | ||||
| $daemon = newv($daemon, array($argv)); | ||||
|  | ||||
| if ($trace_mode) { | ||||
|   $daemon->setTraceMode(); | ||||
| } | ||||
|  | ||||
| if ($trace_memory) { | ||||
|   $daemon->setTraceMemory(); | ||||
| } | ||||
|  | ||||
| if ($verbose) { | ||||
|   $daemon->setVerbose(true); | ||||
| } | ||||
|  | ||||
| $down_duration = idx($config, 'down'); | ||||
| if ($down_duration) { | ||||
|   $daemon->setScaledownDuration($down_duration); | ||||
| } | ||||
|  | ||||
| $daemon->execute(); | ||||
							
								
								
									
										6
									
								
								scripts/gitadmin/gitx-ssh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| #!/usr/local/bin/bash | ||||
|  | ||||
| set -e | ||||
| set -u | ||||
|  | ||||
| ssh -i $SSH_KEYFILE $@ | ||||
							
								
								
									
										316
									
								
								scripts/gitadmin/rebuild_gitadmin.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,316 @@ | ||||
| #!/usr/local/bin/php | ||||
| <?php | ||||
|  | ||||
| $root = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $root.'/scripts/__init_script__.php'; | ||||
|  | ||||
| function escape_name($name) { | ||||
|   return preg_replace('/[^A-Za-z0-9\-]/', '_', $name); | ||||
| } | ||||
|  | ||||
| function startswith($string, $prefix) { | ||||
|   return substr($string, 0, strlen($prefix)) == $prefix; | ||||
| } | ||||
|  | ||||
| function endswith($string, $suffix) { | ||||
|   $suffix_length = strlen($suffix); | ||||
|   return substr($string, strlen($string) - $suffix_length, | ||||
|                 $suffix_length) == $suffix; | ||||
| } | ||||
|  | ||||
| function write_ini_file($array, $file) { | ||||
|   $res = array(); | ||||
|   foreach ($array as $key => $val) { | ||||
|     if (is_array($val)) { | ||||
|       $res[] = "[$key]"; | ||||
|       foreach ($val as $skey => $sval) { | ||||
|         $res[] = "$skey = $sval"; | ||||
|       } | ||||
|       $res[] = ''; | ||||
|     } else { | ||||
|       $res[] = "$key = $val"; | ||||
|     } | ||||
|   } | ||||
|   file_put_contents($file, implode("\n", $res)); | ||||
| } | ||||
|  | ||||
| // Get user's heys and put them to the configuration | ||||
| function handleSingleUserPHID( | ||||
|   $keydir, $viewer, $userPHID, $system_keys, &$used_keys) { | ||||
|   $user = id(new PhabricatorPeopleQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withPHIDs(array($userPHID)) | ||||
|     ->executeOne(); | ||||
|   if (!$user) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   if ($user->getIsDisabled()) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   $keys = id(new PhabricatorAuthSSHKey())->loadAllWhere( | ||||
|    'objectPHID = %s', | ||||
|     $user->getPHID()); | ||||
|  | ||||
|   $members = array(); | ||||
|   foreach ($keys as $key) { | ||||
|       $full_key_content = | ||||
|         $key->getKeyType().' '. | ||||
|         $key->getKeyBody().' '. | ||||
|         $key->getKeyComment()."\n"; | ||||
|  | ||||
|     if (array_key_exists($full_key_content, $system_keys)) { | ||||
|       $members[] = $system_keys[$full_key_content]; | ||||
|     } else { | ||||
|       $escaped_key_name = escape_name($key->getName()); | ||||
|       $member = 'PHAB_'.$user->getUserName(). | ||||
|         '_'.$escaped_key_name. | ||||
|         '_'.$key->getID(); | ||||
|       $members[] = $member; | ||||
|       if (!array_key_exists($member, $used_keys)) { | ||||
|         $used_keys[$member] = true; | ||||
|         file_put_contents("$keydir/$member.pub", $full_key_content); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleCustomPolicy( | ||||
|   $keydir, $viewer, $policy, $system_keys, &$used_keys) { | ||||
|   $members = array(); | ||||
|   $rules = $policy->getRules(); | ||||
|   foreach ($rules as $rule) { | ||||
|     // Everyone is denied by default anyway | ||||
|     if ($rule['action'] == 'allow') { | ||||
|       if ($rule['rule'] == 'PhabricatorPolicyRuleUsers') { | ||||
|         foreach ($rule['value'] as $userPHID) { | ||||
|           $members = array_merge($members, | ||||
|               handleSingleUserPHID($keydir, $viewer, $userPHID, | ||||
|                                    $system_keys, $used_keys)); | ||||
|         } | ||||
|       } else { | ||||
|         /* pass */ | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| // Parse repository and put it's members to the config file | ||||
| function handleSingleRepository( | ||||
|   $keydir, $viewer, $repository, $all_repositories, $system_keys, | ||||
|   &$new_configuration, &$used_keys) { | ||||
|   $policies = PhabricatorPolicyQuery::loadPolicies( | ||||
|     $viewer, | ||||
|     $repository); | ||||
|  | ||||
|   $pushable = $policies[DiffusionPushCapability::CAPABILITY]; | ||||
|   $type = $pushable->getType(); | ||||
|  | ||||
|   $members = array(); | ||||
|  | ||||
|   if ($type == PhabricatorPolicyType::TYPE_PROJECT) { | ||||
|     $project = id(new PhabricatorProjectQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->needMembers(true) | ||||
|       ->withPHIDs(array($pushable->getPHID())) | ||||
|       ->executeOne(); | ||||
|  | ||||
|     $memberPHIDs = $project->getMemberPHIDs(); | ||||
|     foreach ($memberPHIDs as $memberPHID) { | ||||
|       $members = array_merge($members, | ||||
|         handleSingleUserPHID($keydir, $viewer, $memberPHID, | ||||
|                              $system_keys, $used_keys)); | ||||
|     } | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_USER) { | ||||
|     $members = handleSingleUserPHID( | ||||
|       $keydir, $viewer, $pushable->getPHID(), $system_keys, $used_keys); | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_CUSTOM) { | ||||
|     $members = handleCustomPolicy( | ||||
|       $keydir, $viewer, $pushable, $system_keys, $used_keys); | ||||
|   } else { | ||||
|     /* pass */ | ||||
|   } | ||||
|  | ||||
|   if (count($members)) { | ||||
|     $uri = $repository->getRemoteURI(); | ||||
|     $repository_name = basename($uri, '.git'); | ||||
|     $escaped_repository_name = escape_name($repository->getName()); | ||||
|     $group_name = "PHAB_${escaped_repository_name}"; | ||||
|     $values = array(); | ||||
|     $values['members'] = join(' ', $members); | ||||
|     $values['readonly'] = join(' ', $all_repositories); | ||||
|     $values['writable'] = $repository_name; | ||||
|     $new_configuration["group $group_name"] = $values; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Remove groups from previous automated configuration built | ||||
| function getCleanOldConfiguration($old_configuration) { | ||||
|   $new_configuration = array(); | ||||
|   foreach ($old_configuration as $group => $values) { | ||||
|     if (!startswith($group, 'group PHAB')) { | ||||
|       $new_configuration[$group] = $values; | ||||
|     } | ||||
|   } | ||||
|   return $new_configuration; | ||||
| } | ||||
|  | ||||
| // Get non-phab keys | ||||
| function getSystemPublicKeys($keydir) { | ||||
|   $files = scandir($keydir); | ||||
|   $system_keys = array(); | ||||
|   foreach ($files as $file) { | ||||
|     if (!startswith($file, "PHAB") && endswith($file, '.pub')) { | ||||
|       $key = file_get_contents("$keydir/$file"); | ||||
|       $system_keys[$key] = basename($file, '.pub'); | ||||
|     } | ||||
|   } | ||||
|   return $system_keys; | ||||
| } | ||||
|  | ||||
| // Remove unused public keys | ||||
| function removeUnusedPublicKeys($keydir, $used_keys) { | ||||
|   $files = scandir($keydir); | ||||
|   foreach ($files as $file) { | ||||
|     if (startswith($file, "PHAB")) { | ||||
|       $member = basename($file, '.pub'); | ||||
|       if (!array_key_exists($member, $used_keys)) { | ||||
|         unlink("$keydir/$file"); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| function rebuildConfiguration($gitosis_root) { | ||||
|   $keydir = "$gitosis_root/keydir"; | ||||
|   $configuration_file = "$gitosis_root/gitosis.conf"; | ||||
|  | ||||
|   if (!file_exists($configuration_file)) { | ||||
|     print("Not found: $configuration_file\n"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   $viewer = id(new PhabricatorUser()) | ||||
|     ->loadOneWhere('username = %s', 'sergey'); | ||||
|  | ||||
|   $old_configuration = parse_ini_file( | ||||
|     $configuration_file, true, INI_SCANNER_RAW); | ||||
|  | ||||
|   $new_configuration = getCleanOldConfiguration( | ||||
|     $old_configuration); | ||||
|  | ||||
|   // Get "system" keys to re-use if phab account uses the | ||||
|   // same public key | ||||
|   $system_keys = getSystemPublicKeys($keydir); | ||||
|  | ||||
|   // Get list of all repos which is awailable for read | ||||
|   $all_repositories = array(); | ||||
|   foreach ($old_configuration as $group => $values) { | ||||
|     if (startswith($group, 'repo')) { | ||||
|       $repository_name = substr($group, 5, strlen($group) - 5); | ||||
|       if ($repository_name == 'gitosis-admin') | ||||
|         continue; | ||||
|       $all_repositories[] = $repository_name; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Fill in new configuration and keys | ||||
|   $used_keys = array(); | ||||
|   $repositories = id(new PhabricatorRepositoryQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->execute(); | ||||
|  | ||||
|   foreach ($repositories as $repository_id => $repository) { | ||||
|     $type = $repository->getVersionControlSystem(); | ||||
|     if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { | ||||
|       handleSingleRepository( | ||||
|         $keydir, $viewer, $repository, $all_repositories, $system_keys, | ||||
|         $new_configuration, $used_keys); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   write_ini_file($new_configuration, $configuration_file); | ||||
|   removeUnusedPublicKeys($keydir, $used_keys); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| function getGitCommand($repository) { | ||||
|   $git_dir = realpath("$repository/.git"); | ||||
|   $git = "git --git-dir='$git_dir'"; | ||||
|   $git .= ' --work-tree='.realpath($repository); | ||||
|   return $git; | ||||
| } | ||||
|  | ||||
| function runGitCommand($repository, $arguments, | ||||
|                        &$output=null, &$return_var=null) { | ||||
|   $git = getGitCommand($repository); | ||||
|   $git .= " $arguments"; | ||||
|   exec($git, $output, $return_var); | ||||
|   return $return_var == 0; | ||||
| } | ||||
|  | ||||
| function runGitSshCommand($repository, $key, $arguments, | ||||
|                        &$output=null, &$return_var=null) { | ||||
|   $gitx_ssh = realpath(dirname(__FILE__) . "/gitx-ssh"); | ||||
|   $abs_key = realpath($key); | ||||
|   $git = "SSH_KEYFILE=$abs_key GIT_SSH=$gitx_ssh "; | ||||
|   $git .= getGitCommand($repository); | ||||
|   $git .= " $arguments"; | ||||
|   exec($git, $output, $return_var); | ||||
|   return $return_var == 0; | ||||
| } | ||||
|  | ||||
| function repositoryPull($repository, $key) { | ||||
|   return runGitSshCommand($repository, $key, 'pull'); | ||||
| } | ||||
|  | ||||
| function repositoryCommitAll($repository, $author, $message) { | ||||
|   if (!runGitCommand( | ||||
|     $repository, 'ls-files --other --exclude-standard', $untracked_files)) { | ||||
|     return false; | ||||
|   } | ||||
|   if (count($untracked_files)) { | ||||
|     $flat_files = join(' ', $untracked_files); | ||||
|     if (!runGitCommand($repository, "add $flat_files")) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   runGitCommand($repository, "update-index -q --refresh", $output); | ||||
|   runGitCommand($repository, "diff-index --name-only HEAD --", $output); | ||||
|   if (count($output)) { | ||||
|     return runGitCommand( | ||||
|       $repository, "commit --author='$author' -a -m '$message'", $output); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| if (count($argv) != 3) { | ||||
|   print("Usage: {$argv[0]} /path/to/gitosis-admin /path/to/id_rsa.pub\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| $gitosis_root = $argv[1]; | ||||
| $key = $argv[2]; | ||||
|  | ||||
| if (!repositoryPull($gitosis_root, $key)) { | ||||
|   print("Failed to pull changes from server.\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| if (!rebuildConfiguration($gitosis_root)) { | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| if (!repositoryCommitAll( | ||||
|   $gitosis_root, 'Rebuild Gitadmin <null@git.blender.org>', | ||||
|   'Update to correspond changes in Phabricator')) { | ||||
|   print("Failed to commit changes.\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| runGitSshCommand($gitosis_root, $key, 'push origin master'); | ||||
| ?> | ||||
| @@ -8,14 +8,10 @@ function init_phabricator_script(array $options) { | ||||
|   ini_set( | ||||
|     'include_path', | ||||
|     $include_path.PATH_SEPARATOR.dirname(__FILE__).'/../../../'); | ||||
|  | ||||
|   $ok = @include_once 'arcanist/support/init/init-script.php'; | ||||
|   if (!$ok) { | ||||
|     echo | ||||
|       'FATAL ERROR: Unable to load the "Arcanist" library. '. | ||||
|       'Put "arcanist/" next to "phabricator/" on disk.'; | ||||
|     echo "\n"; | ||||
|  | ||||
|   @include_once 'libphutil/scripts/__init_script__.php'; | ||||
|   if (!@constant('__LIBPHUTIL__')) { | ||||
|     echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ". | ||||
|       "include the parent directory of libphutil/.\n"; | ||||
|     exit(1); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -22,7 +22,7 @@ failed() { | ||||
| } | ||||
|  | ||||
| ISSUE=`cat /etc/issue` | ||||
| if [[ $ISSUE != Ubuntu* ]] | ||||
| if [[ ($ISSUE != Ubuntu*) && ($ISSUE != Debian*) ]]; | ||||
| then | ||||
|   echo "This script is intended for use on Ubuntu, but this system appears"; | ||||
|   echo "to be something else. Your results may vary."; | ||||
|   | ||||
| @@ -211,29 +211,21 @@ try { | ||||
|     ->setUniqueMethod('getName') | ||||
|     ->execute(); | ||||
|  | ||||
|   $command_list = array_keys($workflows); | ||||
|   $command_list = implode(', ', $command_list); | ||||
|  | ||||
|   $error_lines = array(); | ||||
|   $error_lines[] = pht('Welcome to Phabricator.'); | ||||
|   $error_lines[] = pht( | ||||
|     'You are logged in as %s.', | ||||
|     $user_name); | ||||
|  | ||||
|   if (!$original_argv) { | ||||
|     $error_lines[] = pht( | ||||
|       'You have not specified a command to run. This means you are requesting '. | ||||
|       'an interactive shell, but Phabricator does not provide interactive '. | ||||
|       'shells over SSH.'); | ||||
|     $error_lines[] = pht( | ||||
|       '(Usually, you should run a command like "git clone" or "hg push" '. | ||||
|       'instead of connecting directly with SSH.)'); | ||||
|     $error_lines[] = pht( | ||||
|       'Supported commands are: %s.', | ||||
|       $command_list); | ||||
|  | ||||
|     $error_lines = implode("\n\n", $error_lines); | ||||
|     throw new PhutilArgumentUsageException($error_lines); | ||||
|     throw new Exception( | ||||
|       pht( | ||||
|         "Welcome to Phabricator.\n\n". | ||||
|         "You are logged in as %s.\n\n". | ||||
|         "You haven't specified a command to run. This means you're requesting ". | ||||
|         "an interactive shell, but Phabricator does not provide an ". | ||||
|         "interactive shell over SSH.\n\n". | ||||
|         "Usually, you should run a command like `%s` or `%s` ". | ||||
|         "rather than connecting directly with SSH.\n\n". | ||||
|         "Supported commands are: %s.", | ||||
|         $user_name, | ||||
|         'git clone', | ||||
|         'hg push', | ||||
|         implode(', ', array_keys($workflows)))); | ||||
|   } | ||||
|  | ||||
|   $log_argv = implode(' ', $original_argv); | ||||
| @@ -255,20 +247,7 @@ try { | ||||
|   $parsed_args = new PhutilArgumentParser($parseable_argv); | ||||
|  | ||||
|   if (empty($workflows[$command])) { | ||||
|     $error_lines[] = pht( | ||||
|       'You have specified the command "%s", but that command is not '. | ||||
|       'supported by Phabricator. As received by Phabricator, your entire '. | ||||
|       'argument list was:', | ||||
|       $command); | ||||
|  | ||||
|     $error_lines[] = csprintf('  $ ssh ... -- %Ls', $parseable_argv); | ||||
|  | ||||
|     $error_lines[] = pht( | ||||
|       'Supported commands are: %s.', | ||||
|       $command_list); | ||||
|  | ||||
|     $error_lines = implode("\n\n", $error_lines); | ||||
|     throw new PhutilArgumentUsageException($error_lines); | ||||
|     throw new Exception(pht('Invalid command.')); | ||||
|   } | ||||
|  | ||||
|   $workflow = $parsed_args->parseWorkflows($workflows); | ||||
|   | ||||
							
								
								
									
										61
									
								
								scripts/svnauth/archived_repos.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,61 @@ | ||||
| <?php | ||||
|   $ARCHIVED_REPOS = array( | ||||
|     'abstractmesh', | ||||
|     'beast', | ||||
| #    'bf-blender', | ||||
|     'bf-docboard-es', | ||||
|     'bf-extensions', | ||||
|     'bf-funboard', | ||||
|     'bf-scripts', | ||||
| #    'bf-translations', | ||||
|     'bfct', | ||||
|     'bforge', | ||||
|     'blend-doc', | ||||
|     'blend2cs', | ||||
|     'blendedmidi', | ||||
|     'blendercad', | ||||
|     'blendxml', | ||||
|     'bzoo', | ||||
|     'docboard', | ||||
|     'drqueue', | ||||
|     'ghost', | ||||
|     'girona', | ||||
|     'guiman', | ||||
|     'lsystem', | ||||
|     'magic', | ||||
|     'makeh', | ||||
|     'mechanicblender', | ||||
|     'neverblender', | ||||
|     'news', | ||||
|     'night', | ||||
|     'nitrox', | ||||
|     'osgexport', | ||||
|     'peerrating', | ||||
|     'piovra', | ||||
|     'pyverse', | ||||
|     'qdune', | ||||
|     'scolblender', | ||||
|     'skined', | ||||
|     'smdio', | ||||
|     'soapyblender', | ||||
|     'soc-2005', | ||||
|     'soc-2006', | ||||
|     'soc-2007', | ||||
|     'soc-2008', | ||||
|     'sourceforge', | ||||
|     'spe', | ||||
|     'stats', | ||||
|     'ter2blend', | ||||
|     'torqueexporter', | ||||
|     'tuhopuu', | ||||
|     'tzuray', | ||||
|     'vectex', | ||||
|     'vectorrender', | ||||
|     'verse', | ||||
|     'vrmlimportexp', | ||||
|     'warblender', | ||||
|     'wpyre', | ||||
|     'yafray', | ||||
|     'yofrankie' | ||||
|   ); | ||||
| ?> | ||||
							
								
								
									
										181
									
								
								scripts/svnauth/rebuild_svnauth.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,181 @@ | ||||
| #!/usr/local/bin/php | ||||
| <?php | ||||
|  | ||||
| $root = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $root.'/scripts/__init_script__.php'; | ||||
|  | ||||
| function getSVNRepositoryName($repository) { | ||||
|   $uri = $repository->getRemoteURI(); | ||||
|   return preg_replace( | ||||
|     '/https?\:\/\/.*?\/svnroot\/([^\/]+)\/?.*/', '$1', $uri); | ||||
| } | ||||
|  | ||||
| // Get user's heys and put them to the configuration | ||||
| function handleSingleUserPHID( | ||||
|   $viewer, $userPHID, $repository, &$authfile, &$access) { | ||||
|   $user = id(new PhabricatorPeopleQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withPHIDs(array($userPHID)) | ||||
|     ->executeOne(); | ||||
|   if (!$user) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if ($user->getIsDisabled()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   $user_name = $user->getUserName(); | ||||
|   if (!array_key_exists($user_name, $authfile)) { | ||||
|     $field_list = PhabricatorCustomField::getObjectFields( | ||||
|       $user, | ||||
|       PhabricatorCustomField::ROLE_STORAGE); | ||||
|     $field_list | ||||
|       ->setViewer($user) | ||||
|       ->readFieldsFromStorage($user); | ||||
|     $fields = $field_list->getFields(); | ||||
|     $htaccess_field = idx($fields, 'std:user:htaccess_password_hash'); | ||||
|     if ($htaccess_field) { | ||||
|       $password_hash = $htaccess_field->getValueForStorage(); | ||||
|       $authfile[$user_name] = array('hash' => $password_hash, | ||||
|                                     'email' => $user->loadPrimaryEmailAddress(), | ||||
|                                     'name' => $user->getRealName()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   $repository_name = getSVNRepositoryName($repository); | ||||
|  | ||||
|   $repository_rootpath = $repository_name . ':/'; | ||||
|   if (!array_key_exists($repository_rootpath, $access)) { | ||||
|     $access[$repository_rootpath]['RW'] = array(); | ||||
|     $access[$repository_rootpath]['RO'] = array(); | ||||
|   } | ||||
|   $access[$repository_rootpath]['RO'][] = $user_name; | ||||
|  | ||||
|   // Store write access settings to current subath | ||||
|   $subpath = $repository->getDetail('svn-subpath'); | ||||
|   $subpath = rtrim($subpath, '/'); | ||||
|   $repository_pathname = "$repository_name:/$subpath"; | ||||
|   if (!array_key_exists($repository_pathname, $access)) { | ||||
|     $access[$repository_pathname]['RW'] = array(); | ||||
|     $access[$repository_pathname]['RO'] = array(); | ||||
|   } | ||||
|   $access[$repository_pathname]['RW'][] = $user_name; | ||||
|  | ||||
|   // Write access to the tags | ||||
|   $tags_pathname = "$repository_name:/tags"; | ||||
|   if (!array_key_exists($tags_pathname, $access)) { | ||||
|     $access[$tags_pathname]['RW'] = array(); | ||||
|     $access[$tags_pathname]['RO'] = array(); | ||||
|   } | ||||
|   $access[$tags_pathname]['RW'][] = $user_name; | ||||
|  | ||||
|   // Write access to the branches. | ||||
|   $branches_pathname = "$repository_name:/branches"; | ||||
|   if (!array_key_exists($branches_pathname, $access)) { | ||||
|     $access[$branches_pathname]['RW'] = array(); | ||||
|     $access[$branches_pathname]['RO'] = array(); | ||||
|   } | ||||
|   $access[$branches_pathname]['RW'][] = $user_name; | ||||
| } | ||||
|  | ||||
| // Parse repository and put it's members to the config file | ||||
| function handleSingleRepository( | ||||
|   $viewer, $repository, &$authfile, &$access) { | ||||
|   $policies = PhabricatorPolicyQuery::loadPolicies( | ||||
|     $viewer, | ||||
|     $repository); | ||||
|  | ||||
|   $pushable = $policies[DiffusionPushCapability::CAPABILITY]; | ||||
|   $type = phid_get_type($pushable->getPHID()); | ||||
|  | ||||
|   // Make sure repository is always available for read-only access | ||||
|   $repository_rootpath = getSVNRepositoryName($repository) . ':/'; | ||||
|   if (!array_key_exists($repository_rootpath, $access)) { | ||||
|     $access[$repository_rootpath]['RW'] = array(); | ||||
|     $access[$repository_rootpath]['RO'] = array(); | ||||
|   } | ||||
|  | ||||
|   if ($type == PhabricatorProjectProjectPHIDType::TYPECONST) { | ||||
|     $project = id(new PhabricatorProjectQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->needMembers(true) | ||||
|       ->withPHIDs(array($pushable->getPHID())) | ||||
|       ->executeOne(); | ||||
|  | ||||
|     $memberPHIDs = $project->getMemberPHIDs(); | ||||
|     foreach ($memberPHIDs as $memberPHID) { | ||||
|       handleSingleUserPHID( | ||||
|         $viewer, $memberPHID, $repository, $authfile, $access); | ||||
|     } | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_USER) { | ||||
|     handleSingleUserPHID( | ||||
|       $viewer, $pushable->getPHID(), $repository, $authfile, $access); | ||||
|   } else if ($type == PhabricatorPolicyPHIDTypePolicy::TYPECONST) { | ||||
|     /* pass */ | ||||
|   } else { | ||||
|     /* pass */ | ||||
|   } | ||||
| } | ||||
|  | ||||
| function rebuildConfiguration($what) { | ||||
|   $viewer = id(new PhabricatorUser()) | ||||
|     ->loadOneWhere('username = %s', 'sergey'); | ||||
|  | ||||
|   $repositories = id(new PhabricatorRepositoryQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->execute(); | ||||
|  | ||||
|   $authfile = array(); | ||||
|   $access = array(); | ||||
|  | ||||
|   require_once 'archived_repos.php'; | ||||
|   foreach ($ARCHIVED_REPOS as $repository) { | ||||
|     $repository_pathname = "$repository:/"; | ||||
|     $access[$repository_pathname]['RW'] = array(); | ||||
|     $access[$repository_pathname]['RO'] = array(); | ||||
|   } | ||||
|  | ||||
|   foreach ($repositories as $repository_id => $repository) { | ||||
|     $type = $repository->getVersionControlSystem(); | ||||
|     if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) { | ||||
|       handleSingleRepository( | ||||
|         $viewer, $repository, $authfile, $access); | ||||
|     } | ||||
|   } | ||||
|   if ($what == 'AUTHFILE') { | ||||
|     foreach ($authfile as $user => $data) { | ||||
|       print("$user:${data['hash']}\n"); | ||||
|     } | ||||
|   } else if ($what == 'ACCESS') { | ||||
|     foreach ($access as $repository => $users) { | ||||
|       print("[$repository]\n"); | ||||
|       $rw_users = array(); | ||||
|       foreach ($users['RW'] as $user) { | ||||
|         print("$user = rw\n"); | ||||
|         $rw_users[$user] = true; | ||||
|       } | ||||
|       foreach ($users['RO'] as $user) { | ||||
|         if (!array_key_exists($user, $rw_users)) { | ||||
|           print("$user = r\n"); | ||||
|         } | ||||
|       } | ||||
|       print("anonsvn = r\n"); | ||||
|       print("* = r\n\n"); | ||||
|     } | ||||
|   } else if ($what == 'NAMEMAP') { | ||||
|     foreach ($authfile as $user => $data) { | ||||
|       print("$user\t${data['email']}\t${data['name']}\n"); | ||||
|     } | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| if (count($argv) != 2 || | ||||
|     ($argv[1] != 'ACCESS' && $argv[1] != 'AUTHFILE' && $argv[1] != 'NAMEMAP')) { | ||||
|   print("Usage: {$argv[0]} ACCESS|AUTHFILE|NAMEMAP\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| rebuildConfiguration($argv[1]); | ||||
| ?> | ||||
| @@ -30,7 +30,6 @@ final class AphrontRequest extends Phobject { | ||||
|   private $controller; | ||||
|   private $uriData = array(); | ||||
|   private $cookiePrefix; | ||||
|   private $submitKey; | ||||
|  | ||||
|   public function __construct($host, $path) { | ||||
|     $this->host = $host; | ||||
| @@ -915,19 +914,5 @@ final class AphrontRequest extends Phobject { | ||||
|     return $future; | ||||
|   } | ||||
|  | ||||
|   public function updateEphemeralCookies() { | ||||
|     $submit_cookie = PhabricatorCookies::COOKIE_SUBMIT; | ||||
|  | ||||
|     $submit_key = $this->getCookie($submit_cookie); | ||||
|     if (strlen($submit_key)) { | ||||
|       $this->clearCookie($submit_cookie); | ||||
|       $this->submitKey = $submit_key; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   public function getSubmitKey() { | ||||
|     return $this->submitKey; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,85 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontRoutingMapTestCase | ||||
|   extends PhabricatorTestCase { | ||||
|  | ||||
|   public function testRoutingMaps() { | ||||
|     $count = 0; | ||||
|  | ||||
|     $sites = AphrontSite::getAllSites(); | ||||
|     foreach ($sites as $site) { | ||||
|       $maps = $site->getRoutingMaps(); | ||||
|       foreach ($maps as $map) { | ||||
|         foreach ($map->getRoutes() as $rule => $value) { | ||||
|           $this->assertRoutable($site, $map, array(), $rule, $value); | ||||
|           $count++; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (!$count) { | ||||
|       $this->assertSkipped( | ||||
|         pht('No sites define any routing rules.')); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private function assertRoutable( | ||||
|     AphrontSite $site, | ||||
|     AphrontRoutingMap $map, | ||||
|     array $path, | ||||
|     $rule, | ||||
|     $value) { | ||||
|  | ||||
|     $path[] = $rule; | ||||
|  | ||||
|     $site_description = $site->getDescription(); | ||||
|     $rule_path = implode(' > ', $path); | ||||
|  | ||||
|     $pattern = implode('', $path); | ||||
|     $pattern = '('.$pattern.')'; | ||||
|     $ok = @preg_match($pattern, ''); | ||||
|  | ||||
|     $this->assertTrue( | ||||
|       ($ok !== false), | ||||
|       pht( | ||||
|         'Routing rule ("%s", for site "%s") does not compile into a '. | ||||
|         'valid regular expression.', | ||||
|         $rule_path, | ||||
|         $site_description)); | ||||
|  | ||||
|     if (is_array($value)) { | ||||
|       $this->assertTrue( | ||||
|         (count($value) > 0), | ||||
|         pht( | ||||
|           'Routing rule ("%s", for site "%s") does not have any targets.', | ||||
|           $rule_path, | ||||
|           $site_description)); | ||||
|  | ||||
|       foreach ($value as $sub_rule => $sub_value) { | ||||
|         $this->assertRoutable($site, $map, $path, $sub_rule, $sub_value); | ||||
|       } | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (is_string($value)) { | ||||
|       $this->assertTrue( | ||||
|         class_exists($value), | ||||
|         pht( | ||||
|           'Routing rule ("%s", for site "%s") points at controller ("%s") '. | ||||
|           'which does not exist.', | ||||
|           $rule_path, | ||||
|           $site_description, | ||||
|           $value)); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     $this->assertFailure( | ||||
|       pht( | ||||
|         'Routing rule ("%s", for site "%s") points at unknown value '. | ||||
|         '(of type "%s"), expected a controller class name string.', | ||||
|         $rule_path, | ||||
|         $site_description, | ||||
|         phutil_describe_type($value))); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -27,8 +27,6 @@ final class AphrontApplicationConfiguration | ||||
|     $request->setApplicationConfiguration($this); | ||||
|     $request->setCookiePrefix($cookie_prefix); | ||||
|  | ||||
|     $request->updateEphemeralCookies(); | ||||
|  | ||||
|     return $request; | ||||
|   } | ||||
|  | ||||
| @@ -773,21 +771,12 @@ final class AphrontApplicationConfiguration | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     $raw_input = @file_get_contents('php://input'); | ||||
|     if ($raw_input !== false) { | ||||
|       $base64_input = base64_encode($raw_input); | ||||
|     } else { | ||||
|       $base64_input = null; | ||||
|     } | ||||
|  | ||||
|     $result = array( | ||||
|       'path' => $path, | ||||
|       'params' => $params, | ||||
|       'user' => idx($_SERVER, 'PHP_AUTH_USER'), | ||||
|       'pass' => idx($_SERVER, 'PHP_AUTH_PW'), | ||||
|  | ||||
|       'raw.base64' => $base64_input, | ||||
|  | ||||
|       // This just makes sure that the response compresses well, so reasonable | ||||
|       // algorithms should want to gzip or deflate it. | ||||
|       'filler' => str_repeat('Q', 1024 * 16), | ||||
| @@ -812,22 +801,27 @@ final class AphrontApplicationConfiguration | ||||
|     // if we can. Among other things, this corrects variable names with | ||||
|     // the "." character in them, which PHP normally converts into "_". | ||||
|  | ||||
|     // If "enable_post_data_reading" is on, the documentation suggests we | ||||
|     // can not read the body. In practice, we seem to be able to. This may | ||||
|     // need to be resolved at some point, likely by instructing installs | ||||
|     // to disable this option. | ||||
|     // There are two major considerations here: whether the | ||||
|     // `enable_post_data_reading` option is set, and whether the content | ||||
|     // type is "multipart/form-data" or not. | ||||
|  | ||||
|     // If `enable_post_data_reading` is off, we're free to read the entire | ||||
|     // raw request body and parse it -- and we must, because $_POST and | ||||
|     // $_FILES are not built for us. If `enable_post_data_reading` is on, | ||||
|     // which is the default, we may not be able to read the body (the | ||||
|     // documentation says we can't, but empirically we can at least some | ||||
|     // of the time). | ||||
|  | ||||
|     // If the content type is "multipart/form-data", we need to build both | ||||
|     // $_POST and $_FILES, which is involved. The body itself is also more | ||||
|     // difficult to parse than other requests. | ||||
|  | ||||
|     $raw_input = PhabricatorStartup::getRawInput(); | ||||
|     $parser = new PhutilQueryStringParser(); | ||||
|  | ||||
|     if (strlen($raw_input)) { | ||||
|       $content_type = idx($_SERVER, 'CONTENT_TYPE'); | ||||
|       $is_multipart = preg_match('@^multipart/form-data@i', $content_type); | ||||
|       if ($is_multipart) { | ||||
|       if ($is_multipart && !ini_get('enable_post_data_reading')) { | ||||
|         $multipart_parser = id(new AphrontMultipartParser()) | ||||
|           ->setContentType($content_type); | ||||
|  | ||||
|   | ||||
| @@ -1,150 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontHTTPHeaderParser extends Phobject { | ||||
|  | ||||
|   private $name; | ||||
|   private $content; | ||||
|   private $pairs; | ||||
|  | ||||
|   public function parseRawHeader($raw_header) { | ||||
|     $this->name = null; | ||||
|     $this->content = null; | ||||
|  | ||||
|     $parts = explode(':', $raw_header, 2); | ||||
|     $this->name = trim($parts[0]); | ||||
|     if (count($parts) > 1) { | ||||
|       $this->content = trim($parts[1]); | ||||
|     } | ||||
|  | ||||
|     $this->pairs = null; | ||||
|  | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getHeaderName() { | ||||
|     $this->requireParse(); | ||||
|     return $this->name; | ||||
|   } | ||||
|  | ||||
|   public function getHeaderContent() { | ||||
|     $this->requireParse(); | ||||
|     return $this->content; | ||||
|   } | ||||
|  | ||||
|   public function getHeaderContentAsPairs() { | ||||
|     $content = $this->getHeaderContent(); | ||||
|  | ||||
|  | ||||
|     $state = 'prekey'; | ||||
|     $length = strlen($content); | ||||
|  | ||||
|     $pair_name = null; | ||||
|     $pair_value = null; | ||||
|  | ||||
|     $pairs = array(); | ||||
|     $ii = 0; | ||||
|     while ($ii < $length) { | ||||
|       $c = $content[$ii]; | ||||
|  | ||||
|       switch ($state) { | ||||
|         case 'prekey'; | ||||
|           // We're eating space in front of a key. | ||||
|           if ($c == ' ') { | ||||
|             $ii++; | ||||
|             break; | ||||
|           } | ||||
|           $pair_name = ''; | ||||
|           $state = 'key'; | ||||
|           break; | ||||
|         case 'key'; | ||||
|           // We're parsing a key name until we find "=" or ";". | ||||
|           if ($c == ';') { | ||||
|             $state = 'done'; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           if ($c == '=') { | ||||
|             $ii++; | ||||
|             $state = 'value'; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           $ii++; | ||||
|           $pair_name .= $c; | ||||
|           break; | ||||
|         case 'value': | ||||
|           // We found an "=", so now figure out if the value is quoted | ||||
|           // or not. | ||||
|           if ($c == '"') { | ||||
|             $ii++; | ||||
|             $state = 'quoted'; | ||||
|             break; | ||||
|           } | ||||
|           $state = 'unquoted'; | ||||
|           break; | ||||
|         case 'quoted': | ||||
|           // We're in a quoted string, parse until we find the closing quote. | ||||
|           if ($c == '"') { | ||||
|             $ii++; | ||||
|             $state = 'done'; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           $ii++; | ||||
|           $pair_value .= $c; | ||||
|           break; | ||||
|         case 'unquoted': | ||||
|           // We're in an unquoted string, parse until we find a space or a | ||||
|           // semicolon. | ||||
|           if ($c == ' ' || $c == ';') { | ||||
|             $state = 'done'; | ||||
|             break; | ||||
|           } | ||||
|           $ii++; | ||||
|           $pair_value .= $c; | ||||
|           break; | ||||
|         case 'done': | ||||
|           // We parsed something, so eat any trailing whitespace and semicolons | ||||
|           // and look for a new value. | ||||
|           if ($c == ' ' || $c == ';') { | ||||
|             $ii++; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           $pairs[] = array( | ||||
|             $pair_name, | ||||
|             $pair_value, | ||||
|           ); | ||||
|  | ||||
|           $pair_name = null; | ||||
|           $pair_value = null; | ||||
|  | ||||
|           $state = 'prekey'; | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if ($state == 'quoted') { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Header has unterminated double quote for key "%s".', | ||||
|           $pair_name)); | ||||
|     } | ||||
|  | ||||
|     if ($pair_name !== null) { | ||||
|       $pairs[] = array( | ||||
|         $pair_name, | ||||
|         $pair_value, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return $pairs; | ||||
|   } | ||||
|  | ||||
|   private function requireParse() { | ||||
|     if ($this->name === null) { | ||||
|       throw new PhutilInvalidStateException('parseRawHeader'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,108 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontHTTPHeaderParserTestCase extends PhutilTestCase { | ||||
|  | ||||
|   public function testHeaderParser() { | ||||
|     $cases = array( | ||||
|       array( | ||||
|         'Key: x; y; z', | ||||
|         'Key', | ||||
|         'x; y; z', | ||||
|         array( | ||||
|           array('x', null), | ||||
|           array('y', null), | ||||
|           array('z', null), | ||||
|         ), | ||||
|       ), | ||||
|       array( | ||||
|         'Content-Disposition: form-data; name="label"', | ||||
|         'Content-Disposition', | ||||
|         'form-data; name="label"', | ||||
|         array( | ||||
|           array('form-data', null), | ||||
|           array('name', 'label'), | ||||
|         ), | ||||
|       ), | ||||
|       array( | ||||
|         'Content-Type: multipart/form-data; charset=utf-8', | ||||
|         'Content-Type', | ||||
|         'multipart/form-data; charset=utf-8', | ||||
|         array( | ||||
|           array('multipart/form-data', null), | ||||
|           array('charset', 'utf-8'), | ||||
|         ), | ||||
|       ), | ||||
|       array( | ||||
|         'Content-Type: application/octet-stream; charset="ut', | ||||
|         'Content-Type', | ||||
|         'application/octet-stream; charset="ut', | ||||
|         false, | ||||
|       ), | ||||
|       array( | ||||
|         'Content-Type: multipart/form-data; boundary=ABCDEFG', | ||||
|         'Content-Type', | ||||
|         'multipart/form-data; boundary=ABCDEFG', | ||||
|         array( | ||||
|           array('multipart/form-data', null), | ||||
|           array('boundary', 'ABCDEFG'), | ||||
|         ), | ||||
|       ), | ||||
|       array( | ||||
|         'Content-Type: multipart/form-data; boundary="ABCDEFG"', | ||||
|         'Content-Type', | ||||
|         'multipart/form-data; boundary="ABCDEFG"', | ||||
|         array( | ||||
|           array('multipart/form-data', null), | ||||
|           array('boundary', 'ABCDEFG'), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
|     foreach ($cases as $case) { | ||||
|       $input = $case[0]; | ||||
|       $expect_name = $case[1]; | ||||
|       $expect_content = $case[2]; | ||||
|  | ||||
|       $parser = id(new AphrontHTTPHeaderParser()) | ||||
|         ->parseRawHeader($input); | ||||
|  | ||||
|       $actual_name = $parser->getHeaderName(); | ||||
|       $actual_content = $parser->getHeaderContent(); | ||||
|  | ||||
|       $this->assertEqual( | ||||
|         $expect_name, | ||||
|         $actual_name, | ||||
|         pht('Header name for: %s', $input)); | ||||
|  | ||||
|       $this->assertEqual( | ||||
|         $expect_content, | ||||
|         $actual_content, | ||||
|         pht('Header content for: %s', $input)); | ||||
|  | ||||
|       if (isset($case[3])) { | ||||
|         $expect_pairs = $case[3]; | ||||
|  | ||||
|         $caught = null; | ||||
|         try { | ||||
|           $actual_pairs = $parser->getHeaderContentAsPairs(); | ||||
|         } catch (Exception $ex) { | ||||
|           $caught = $ex; | ||||
|         } | ||||
|  | ||||
|         if ($expect_pairs === false) { | ||||
|           $this->assertEqual( | ||||
|             true, | ||||
|             ($caught instanceof Exception), | ||||
|             pht('Expect exception for header pairs of: %s', $input)); | ||||
|         } else { | ||||
|           $this->assertEqual( | ||||
|             $expect_pairs, | ||||
|             $actual_pairs, | ||||
|             pht('Header pairs for: %s', $input)); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,249 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontMultipartParser extends Phobject { | ||||
|  | ||||
|   private $contentType; | ||||
|   private $boundary; | ||||
|  | ||||
|   private $buffer; | ||||
|   private $body; | ||||
|   private $state; | ||||
|  | ||||
|   private $part; | ||||
|   private $parts; | ||||
|  | ||||
|   public function setContentType($content_type) { | ||||
|     $this->contentType = $content_type; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getContentType() { | ||||
|     return $this->contentType; | ||||
|   } | ||||
|  | ||||
|   public function beginParse() { | ||||
|     $content_type = $this->getContentType(); | ||||
|     if ($content_type === null) { | ||||
|       throw new PhutilInvalidStateException('setContentType'); | ||||
|     } | ||||
|  | ||||
|     if (!preg_match('(^multipart/form-data)', $content_type)) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Expected "multipart/form-data" content type when executing a '. | ||||
|           'multipart body read.')); | ||||
|     } | ||||
|  | ||||
|     $type_parts = preg_split('(\s*;\s*)', $content_type); | ||||
|     $boundary = null; | ||||
|     foreach ($type_parts as $type_part) { | ||||
|       $matches = null; | ||||
|       if (preg_match('(^boundary=(.*))', $type_part, $matches)) { | ||||
|         $boundary = $matches[1]; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if ($boundary === null) { | ||||
|       throw new Exception( | ||||
|         pht('Received "multipart/form-data" request with no "boundary".')); | ||||
|     } | ||||
|  | ||||
|     $this->parts = array(); | ||||
|     $this->part = null; | ||||
|  | ||||
|     $this->buffer = ''; | ||||
|     $this->boundary = $boundary; | ||||
|  | ||||
|     // We're looking for a (usually empty) body before the first boundary. | ||||
|     $this->state = 'bodynewline'; | ||||
|   } | ||||
|  | ||||
|   public function continueParse($bytes) { | ||||
|     $this->buffer .= $bytes; | ||||
|  | ||||
|     $continue = true; | ||||
|     while ($continue) { | ||||
|       switch ($this->state) { | ||||
|         case 'endboundary': | ||||
|           // We've just parsed a boundary. Next, we expect either "--" (which | ||||
|           // indicates we've reached the end of the parts) or "\r\n" (which | ||||
|           // indicates we should read the headers for the next part). | ||||
|  | ||||
|           if (strlen($this->buffer) < 2) { | ||||
|             // We don't have enough bytes yet, so wait for more. | ||||
|             $continue = false; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           if (!strncmp($this->buffer, '--', 2)) { | ||||
|             // This is "--" after a boundary, so we're done. We'll read the | ||||
|             // rest of the body (the "epilogue") and discard it. | ||||
|             $this->buffer = substr($this->buffer, 2); | ||||
|             $this->state = 'epilogue'; | ||||
|  | ||||
|             $this->part = null; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           if (!strncmp($this->buffer, "\r\n", 2)) { | ||||
|             // This is "\r\n" after a boundary, so we're going to going to | ||||
|             // read the headers for a part. | ||||
|             $this->buffer = substr($this->buffer, 2); | ||||
|             $this->state = 'header'; | ||||
|  | ||||
|             // Create the object to hold the part we're about to read. | ||||
|             $part = new AphrontMultipartPart(); | ||||
|             $this->parts[] = $part; | ||||
|             $this->part = $part; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           throw new Exception( | ||||
|             pht('Expected "\r\n" or "--" after multipart data boundary.')); | ||||
|         case 'header': | ||||
|           // We've just parsed a boundary, followed by "\r\n". We are going | ||||
|           // to read the headers for this part. They are in the form of HTTP | ||||
|           // headers and terminated by "\r\n". The section is terminated by | ||||
|           // a line with no header on it. | ||||
|  | ||||
|           if (strlen($this->buffer) < 2) { | ||||
|             // We don't have enough data to find a "\r\n", so wait for more. | ||||
|             $continue = false; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           if (!strncmp("\r\n", $this->buffer, 2)) { | ||||
|             // This line immediately began "\r\n", so we're done with parsing | ||||
|             // headers. Start parsing the body. | ||||
|             $this->buffer = substr($this->buffer, 2); | ||||
|             $this->state = 'body'; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           // This is an actual header, so look for the end of it. | ||||
|           $header_len = strpos($this->buffer, "\r\n"); | ||||
|           if ($header_len === false) { | ||||
|             // We don't have a full header yet, so wait for more data. | ||||
|             $continue = false; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           $header_buf = substr($this->buffer, 0, $header_len); | ||||
|           $this->part->appendRawHeader($header_buf); | ||||
|  | ||||
|           $this->buffer = substr($this->buffer, $header_len + 2); | ||||
|           break; | ||||
|         case 'body': | ||||
|           // We've parsed a boundary and headers, and are parsing the data for | ||||
|           // this part. The data is terminated by "\r\n--", then the boundary. | ||||
|  | ||||
|           // We'll look for "\r\n", then switch to the "bodynewline" state if | ||||
|           // we find it. | ||||
|  | ||||
|           $marker = "\r"; | ||||
|           $marker_pos = strpos($this->buffer, $marker); | ||||
|  | ||||
|           if ($marker_pos === false) { | ||||
|             // There's no "\r" anywhere in the buffer, so we can just read it | ||||
|             // as provided. Then, since we read all the data, we're done until | ||||
|             // we get more. | ||||
|  | ||||
|             // Note that if we're in the preamble, we won't have a "part" | ||||
|             // object and will just discard the data. | ||||
|             if ($this->part) { | ||||
|               $this->part->appendData($this->buffer); | ||||
|             } | ||||
|             $this->buffer = ''; | ||||
|             $continue = false; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           if ($marker_pos > 0) { | ||||
|             // If there are bytes before the "\r", | ||||
|             if ($this->part) { | ||||
|               $this->part->appendData(substr($this->buffer, 0, $marker_pos)); | ||||
|             } | ||||
|             $this->buffer = substr($this->buffer, $marker_pos); | ||||
|           } | ||||
|  | ||||
|           $expect = "\r\n"; | ||||
|           $expect_len = strlen($expect); | ||||
|           if (strlen($this->buffer) < $expect_len) { | ||||
|             // We don't have enough bytes yet to know if this is "\r\n" | ||||
|             // or not. | ||||
|             $continue = false; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           if (strncmp($this->buffer, $expect, $expect_len)) { | ||||
|             // The next two bytes aren't "\r\n", so eat them and go looking | ||||
|             // for more newlines. | ||||
|             if ($this->part) { | ||||
|               $this->part->appendData(substr($this->buffer, 0, $expect_len)); | ||||
|             } | ||||
|             $this->buffer = substr($this->buffer, $expect_len); | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           // Eat the "\r\n". | ||||
|           $this->buffer = substr($this->buffer, $expect_len); | ||||
|           $this->state = 'bodynewline'; | ||||
|           break; | ||||
|         case 'bodynewline': | ||||
|           // We've parsed a newline in a body, or we just started parsing the | ||||
|           // request. In either case, we're looking for "--", then the boundary. | ||||
|           // If we find it, this section is done. If we don't, we consume the | ||||
|           // bytes and move on. | ||||
|  | ||||
|           $expect = '--'.$this->boundary; | ||||
|           $expect_len = strlen($expect); | ||||
|  | ||||
|           if (strlen($this->buffer) < $expect_len) { | ||||
|             // We don't have enough bytes yet, so wait for more. | ||||
|             $continue = false; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           if (strncmp($this->buffer, $expect, $expect_len)) { | ||||
|             // This wasn't the boundary, so return to the "body" state and | ||||
|             // consume it. (But first, we need to append the "\r\n" which we | ||||
|             // ate earlier.) | ||||
|             if ($this->part) { | ||||
|               $this->part->appendData("\r\n"); | ||||
|             } | ||||
|             $this->state = 'body'; | ||||
|             break; | ||||
|           } | ||||
|  | ||||
|           // This is the boundary, so toss it and move on. | ||||
|           $this->buffer = substr($this->buffer, $expect_len); | ||||
|           $this->state = 'endboundary'; | ||||
|           break; | ||||
|         case 'epilogue': | ||||
|           // We just discard any epilogue. | ||||
|           $this->buffer = ''; | ||||
|           $continue = false; | ||||
|           break; | ||||
|         default: | ||||
|           throw new Exception( | ||||
|             pht( | ||||
|               'Unknown parser state "%s".\n', | ||||
|               $this->state)); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public function endParse() { | ||||
|     if ($this->state !== 'epilogue') { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Expected "multipart/form-data" parse to end '. | ||||
|           'in state "epilogue".')); | ||||
|     } | ||||
|  | ||||
|     return $this->parts; | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,96 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontMultipartPart extends Phobject { | ||||
|  | ||||
|   private $headers = array(); | ||||
|   private $value = ''; | ||||
|  | ||||
|   private $name; | ||||
|   private $filename; | ||||
|   private $tempFile; | ||||
|   private $byteSize = 0; | ||||
|  | ||||
|   public function appendRawHeader($bytes) { | ||||
|     $parser = id(new AphrontHTTPHeaderParser()) | ||||
|       ->parseRawHeader($bytes); | ||||
|  | ||||
|     $header_name = $parser->getHeaderName(); | ||||
|  | ||||
|     $this->headers[] = array( | ||||
|       $header_name, | ||||
|       $parser->getHeaderContent(), | ||||
|     ); | ||||
|  | ||||
|     if (strtolower($header_name) === 'content-disposition') { | ||||
|       $pairs = $parser->getHeaderContentAsPairs(); | ||||
|       foreach ($pairs as $pair) { | ||||
|         list($key, $value) = $pair; | ||||
|         switch ($key) { | ||||
|           case 'filename': | ||||
|             $this->filename = $value; | ||||
|             break; | ||||
|           case 'name': | ||||
|             $this->name = $value; | ||||
|             break; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function appendData($bytes) { | ||||
|     $this->byteSize += strlen($bytes); | ||||
|  | ||||
|     if ($this->isVariable()) { | ||||
|       $this->value .= $bytes; | ||||
|     } else { | ||||
|       if (!$this->tempFile) { | ||||
|         $this->tempFile = new TempFile(getmypid().'.upload'); | ||||
|       } | ||||
|       Filesystem::appendFile($this->tempFile, $bytes); | ||||
|     } | ||||
|  | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function isVariable() { | ||||
|     return ($this->filename === null); | ||||
|   } | ||||
|  | ||||
|   public function getName() { | ||||
|     return $this->name; | ||||
|   } | ||||
|  | ||||
|   public function getVariableValue() { | ||||
|     if (!$this->isVariable()) { | ||||
|       throw new Exception(pht('This part is not a variable!')); | ||||
|     } | ||||
|  | ||||
|     return $this->value; | ||||
|   } | ||||
|  | ||||
|   public function getPHPFileDictionary() { | ||||
|     if (!$this->tempFile) { | ||||
|       $this->appendData(''); | ||||
|     } | ||||
|  | ||||
|     $mime_type = 'application/octet-stream'; | ||||
|     foreach ($this->headers as $header) { | ||||
|       list($name, $value) = $header; | ||||
|       if (strtolower($name) == 'content-type') { | ||||
|         $mime_type = $value; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return array( | ||||
|       'name' => $this->filename, | ||||
|       'type' => $mime_type, | ||||
|       'tmp_name' => (string)$this->tempFile, | ||||
|       'error' => 0, | ||||
|       'size' => $this->byteSize, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontMultipartParserTestCase extends PhutilTestCase { | ||||
|  | ||||
|   public function testParser() { | ||||
|     $map = array( | ||||
|       array( | ||||
|         'data' => 'simple.txt', | ||||
|         'variables' => array( | ||||
|           array('a', 'b'), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
|     $data_dir = dirname(__FILE__).'/data/'; | ||||
|     foreach ($map as $test_case) { | ||||
|       $data = Filesystem::readFile($data_dir.$test_case['data']); | ||||
|       $data = str_replace("\n", "\r\n", $data); | ||||
|  | ||||
|       $parser = id(new AphrontMultipartParser()) | ||||
|         ->setContentType('multipart/form-data; boundary=ABCDEFG'); | ||||
|       $parser->beginParse(); | ||||
|       $parser->continueParse($data); | ||||
|       $parts = $parser->endParse(); | ||||
|  | ||||
|       $variables = array(); | ||||
|       foreach ($parts as $part) { | ||||
|         if (!$part->isVariable()) { | ||||
|           continue; | ||||
|         } | ||||
|  | ||||
|         $variables[] = array( | ||||
|           $part->getName(), | ||||
|           $part->getVariableValue(), | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       $expect_variables = idx($test_case, 'variables', array()); | ||||
|       $this->assertEqual($expect_variables, $variables); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| --ABCDEFG | ||||
| Content-Disposition: form-data; name="a" | ||||
|  | ||||
| b | ||||
| --ABCDEFG-- | ||||
| @@ -1,113 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontRequestStream extends Phobject { | ||||
|  | ||||
|   private $encoding; | ||||
|   private $stream; | ||||
|   private $closed; | ||||
|   private $iterator; | ||||
|  | ||||
|   public function setEncoding($encoding) { | ||||
|     $this->encoding = $encoding; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getEncoding() { | ||||
|     return $this->encoding; | ||||
|   } | ||||
|  | ||||
|   public function getIterator() { | ||||
|     if (!$this->iterator) { | ||||
|       $this->iterator = new PhutilStreamIterator($this->getStream()); | ||||
|     } | ||||
|     return $this->iterator; | ||||
|   } | ||||
|  | ||||
|   public function readData() { | ||||
|     if (!$this->iterator) { | ||||
|       $iterator = $this->getIterator(); | ||||
|       $iterator->rewind(); | ||||
|     } else { | ||||
|       $iterator = $this->getIterator(); | ||||
|     } | ||||
|  | ||||
|     if (!$iterator->valid()) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     $data = $iterator->current(); | ||||
|     $iterator->next(); | ||||
|  | ||||
|     return $data; | ||||
|   } | ||||
|  | ||||
|   private function getStream() { | ||||
|     if (!$this->stream) { | ||||
|       $this->stream = $this->newStream(); | ||||
|     } | ||||
|  | ||||
|     return $this->stream; | ||||
|   } | ||||
|  | ||||
|   private function newStream() { | ||||
|     $stream = fopen('php://input', 'rb'); | ||||
|     if (!$stream) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Failed to open stream "%s" for reading.', | ||||
|           'php://input')); | ||||
|     } | ||||
|  | ||||
|     $encoding = $this->getEncoding(); | ||||
|     if ($encoding === 'gzip') { | ||||
|       // This parameter is magic. Values 0-15 express a time/memory tradeoff, | ||||
|       // but the largest value (15) corresponds to only 32KB of memory and | ||||
|       // data encoded with a smaller window size than the one we pass can not | ||||
|       // be decompressed. Always pass the maximum window size. | ||||
|  | ||||
|       // Additionally, you can add 16 (to enable gzip) or 32 (to enable both | ||||
|       // gzip and zlib). Add 32 to support both. | ||||
|       $zlib_window = 15 + 32; | ||||
|  | ||||
|       $ok = stream_filter_append( | ||||
|         $stream, | ||||
|         'zlib.inflate', | ||||
|         STREAM_FILTER_READ, | ||||
|         array( | ||||
|           'window' => $zlib_window, | ||||
|         )); | ||||
|       if (!$ok) { | ||||
|         throw new Exception( | ||||
|           pht( | ||||
|             'Failed to append filter "%s" to input stream while processing '. | ||||
|             'a request with "%s" encoding.', | ||||
|             'zlib.inflate', | ||||
|             $encoding)); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $stream; | ||||
|   } | ||||
|  | ||||
|   public static function supportsGzip() { | ||||
|     if (!function_exists('gzencode') || !function_exists('gzdecode')) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     $has_zlib = false; | ||||
|  | ||||
|     // NOTE: At least locally, this returns "zlib.*", which is not terribly | ||||
|     // reassuring. We care about "zlib.inflate". | ||||
|  | ||||
|     $filters = stream_get_filters(); | ||||
|     foreach ($filters as $filter) { | ||||
|       if (!strncasecmp($filter, 'zlib.', strlen('zlib.'))) { | ||||
|         $has_zlib = true; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $has_zlib; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -61,17 +61,11 @@ final class AphrontHTTPProxyResponse extends AphrontResponse { | ||||
|     // Strip "Transfer-Encoding" headers. Particularly, the server we proxied | ||||
|     // may have chunked the response, but cURL will already have un-chunked it. | ||||
|     // If we emit the header and unchunked data, the response becomes invalid. | ||||
|  | ||||
|     // See T13517. Strip "Content-Encoding" and "Content-Length" headers, since | ||||
|     // they may reflect compressed content. | ||||
|  | ||||
|     foreach ($headers as $key => $header) { | ||||
|       list($header_head, $header_body) = $header; | ||||
|       $header_head = phutil_utf8_strtolower($header_head); | ||||
|       switch ($header_head) { | ||||
|         case 'transfer-encoding': | ||||
|         case 'content-encoding': | ||||
|         case 'content-length': | ||||
|           unset($headers[$key]); | ||||
|           break; | ||||
|       } | ||||
|   | ||||
| @@ -31,10 +31,10 @@ final class AphrontJSONResponse extends AphrontResponse { | ||||
|   } | ||||
|  | ||||
|   public function getHeaders() { | ||||
|     $headers = parent::getHeaders(); | ||||
|  | ||||
|     $headers[] = array('Content-Type', 'application/json'); | ||||
|  | ||||
|     $headers = array( | ||||
|       array('Content-Type', 'application/json'), | ||||
|     ); | ||||
|     $headers = array_merge(parent::getHeaders(), $headers); | ||||
|     return $headers; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ abstract class AphrontResponse extends Phobject { | ||||
|   private $contentSecurityPolicyURIs; | ||||
|   private $disableContentSecurityPolicy; | ||||
|   protected $frameable; | ||||
|   private $headers = array(); | ||||
|  | ||||
|  | ||||
|   public function setRequest($request) { | ||||
|     $this->request = $request; | ||||
| @@ -49,11 +49,6 @@ abstract class AphrontResponse extends Phobject { | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   final public function addHeader($key, $value) { | ||||
|     $this->headers[] = array($key, $value); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|  | ||||
| /* -(  Content  )------------------------------------------------------------ */ | ||||
|  | ||||
| @@ -110,10 +105,6 @@ abstract class AphrontResponse extends Phobject { | ||||
|  | ||||
|     $headers[] = array('Referrer-Policy', 'no-referrer'); | ||||
|  | ||||
|     foreach ($this->headers as $header) { | ||||
|       $headers[] = $header; | ||||
|     } | ||||
|  | ||||
|     return $headers; | ||||
|   } | ||||
|  | ||||
| @@ -426,11 +417,6 @@ abstract class AphrontResponse extends Phobject { | ||||
|   } | ||||
|  | ||||
|   public function willBeginWrite() { | ||||
|     // If we've already sent headers, these "ini_set()" calls will warn that | ||||
|     // they have no effect. Today, this always happens because we're inside | ||||
|     // a unit test, so just skip adjusting the setting. | ||||
|  | ||||
|     if (!headers_sent()) { | ||||
|     if ($this->shouldCompressResponse()) { | ||||
|       // Enable automatic compression here. Webservers sometimes do this for | ||||
|       // us, but we now detect the absence of compression and warn users about | ||||
| @@ -440,7 +426,6 @@ abstract class AphrontResponse extends Phobject { | ||||
|       ini_set('zlib.output_compression', 0); | ||||
|     } | ||||
|   } | ||||
|   } | ||||
|  | ||||
|   public function didCompleteWrite($aborted) { | ||||
|     return; | ||||
|   | ||||
| @@ -1,76 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * NOTE: This is very new and unstable. | ||||
|  */ | ||||
| final class PhutilSprite extends Phobject { | ||||
|  | ||||
|   private $sourceFiles = array(); | ||||
|   private $sourceX; | ||||
|   private $sourceY; | ||||
|   private $sourceW; | ||||
|   private $sourceH; | ||||
|   private $targetCSS; | ||||
|   private $spriteSheet; | ||||
|   private $name; | ||||
|  | ||||
|   public function setName($name) { | ||||
|     $this->name = $name; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getName() { | ||||
|     return $this->name; | ||||
|   } | ||||
|  | ||||
|   public function setTargetCSS($target_css) { | ||||
|     $this->targetCSS = $target_css; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getTargetCSS() { | ||||
|     return $this->targetCSS; | ||||
|   } | ||||
|  | ||||
|   public function setSourcePosition($x, $y) { | ||||
|     $this->sourceX = $x; | ||||
|     $this->sourceY = $y; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function setSourceSize($w, $h) { | ||||
|     $this->sourceW = $w; | ||||
|     $this->sourceH = $h; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getSourceH() { | ||||
|     return $this->sourceH; | ||||
|   } | ||||
|  | ||||
|   public function getSourceW() { | ||||
|     return $this->sourceW; | ||||
|   } | ||||
|  | ||||
|   public function getSourceY() { | ||||
|     return $this->sourceY; | ||||
|   } | ||||
|  | ||||
|   public function getSourceX() { | ||||
|     return $this->sourceX; | ||||
|   } | ||||
|  | ||||
|   public function setSourceFile($source_file, $scale = 1) { | ||||
|     $this->sourceFiles[$scale] = $source_file; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getSourceFile($scale) { | ||||
|     if (empty($this->sourceFiles[$scale])) { | ||||
|       throw new Exception(pht("No source file for scale '%s'!", $scale)); | ||||
|     } | ||||
|  | ||||
|     return $this->sourceFiles[$scale]; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,385 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * NOTE: This is very new and unstable. | ||||
|  */ | ||||
| final class PhutilSpriteSheet extends Phobject { | ||||
|  | ||||
|   const MANIFEST_VERSION = 1; | ||||
|  | ||||
|   const TYPE_STANDARD = 'standard'; | ||||
|   const TYPE_REPEAT_X = 'repeat-x'; | ||||
|   const TYPE_REPEAT_Y = 'repeat-y'; | ||||
|  | ||||
|   private $sprites = array(); | ||||
|   private $sources = array(); | ||||
|   private $hashes  = array(); | ||||
|   private $cssHeader; | ||||
|   private $generated; | ||||
|   private $scales = array(1); | ||||
|   private $type = self::TYPE_STANDARD; | ||||
|   private $basePath; | ||||
|  | ||||
|   private $css; | ||||
|   private $images; | ||||
|  | ||||
|   public function addSprite(PhutilSprite $sprite) { | ||||
|     $this->generated = false; | ||||
|     $this->sprites[] = $sprite; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function setCSSHeader($header) { | ||||
|     $this->generated = false; | ||||
|     $this->cssHeader = $header; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function setScales(array $scales) { | ||||
|     $this->scales = array_values($scales); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getScales() { | ||||
|     return $this->scales; | ||||
|   } | ||||
|  | ||||
|   public function setSheetType($type) { | ||||
|     $this->type = $type; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function setBasePath($base_path) { | ||||
|     $this->basePath = $base_path; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   private function generate() { | ||||
|     if ($this->generated) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     $multi_row = true; | ||||
|     $multi_col = true; | ||||
|     $margin_w = 1; | ||||
|     $margin_h = 1; | ||||
|  | ||||
|     $type = $this->type; | ||||
|     switch ($type) { | ||||
|       case self::TYPE_STANDARD: | ||||
|         break; | ||||
|       case self::TYPE_REPEAT_X: | ||||
|         $multi_col = false; | ||||
|         $margin_w = 0; | ||||
|  | ||||
|         $width = null; | ||||
|         foreach ($this->sprites as $sprite) { | ||||
|           if ($width === null) { | ||||
|             $width = $sprite->getSourceW(); | ||||
|           } else if ($width !== $sprite->getSourceW()) { | ||||
|             throw new Exception( | ||||
|               pht( | ||||
|                 "All sprites in a '%s' sheet must have the same width.", | ||||
|                 'repeat-x')); | ||||
|           } | ||||
|         } | ||||
|         break; | ||||
|       case self::TYPE_REPEAT_Y: | ||||
|         $multi_row = false; | ||||
|         $margin_h = 0; | ||||
|  | ||||
|         $height = null; | ||||
|         foreach ($this->sprites as $sprite) { | ||||
|           if ($height === null) { | ||||
|             $height = $sprite->getSourceH(); | ||||
|           } else if ($height !== $sprite->getSourceH()) { | ||||
|             throw new Exception( | ||||
|               pht( | ||||
|                 "All sprites in a '%s' sheet must have the same height.", | ||||
|                 'repeat-y')); | ||||
|           } | ||||
|         } | ||||
|         break; | ||||
|       default: | ||||
|         throw new Exception(pht("Unknown sprite sheet type '%s'!", $type)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     $css = array(); | ||||
|     if ($this->cssHeader) { | ||||
|       $css[] = $this->cssHeader; | ||||
|     } | ||||
|  | ||||
|     $out_w = 0; | ||||
|     $out_h = 0; | ||||
|  | ||||
|     // Lay out the sprite sheet. We attempt to build a roughly square sheet | ||||
|     // so it's easier to manage, since 2000x20 is more cumbersome for humans | ||||
|     // to deal with than 200x200. | ||||
|     // | ||||
|     // To do this, we use a simple greedy algorithm, adding sprites one at a | ||||
|     // time. For each sprite, if the sheet is at least as wide as it is tall | ||||
|     // we create a new row. Otherwise, we try to add it to an existing row. | ||||
|     // | ||||
|     // This isn't optimal, but does a reasonable job in most cases and isn't | ||||
|     // too messy. | ||||
|  | ||||
|     // Group the sprites by their sizes. We lay them out in the sheet as | ||||
|     // boxes, but then put them into the boxes in the order they were added | ||||
|     // so similar sprites end up nearby on the final sheet. | ||||
|     $boxes = array(); | ||||
|     foreach (array_reverse($this->sprites) as $sprite) { | ||||
|       $s_w = $sprite->getSourceW() + $margin_w; | ||||
|       $s_h = $sprite->getSourceH() + $margin_h; | ||||
|       $boxes[$s_w][$s_h][] = $sprite; | ||||
|     } | ||||
|  | ||||
|     $rows = array(); | ||||
|     foreach ($this->sprites as $sprite) { | ||||
|       $s_w = $sprite->getSourceW() + $margin_w; | ||||
|       $s_h = $sprite->getSourceH() + $margin_h; | ||||
|  | ||||
|       // Choose a row for this sprite. | ||||
|       $maybe = array(); | ||||
|       foreach ($rows as $key => $row) { | ||||
|         if ($row['h'] < $s_h) { | ||||
|           // We can only add it to a row if the row is at least as tall as the | ||||
|           // sprite. | ||||
|           continue; | ||||
|         } | ||||
|         // We prefer rows which have the same height as the sprite, and then | ||||
|         // rows which aren't yet very wide. | ||||
|         $wasted_v = ($row['h'] - $s_h); | ||||
|         $wasted_h = ($row['w'] / $out_w); | ||||
|         $maybe[$key] = $wasted_v + $wasted_h; | ||||
|       } | ||||
|  | ||||
|       $row_key = null; | ||||
|       if ($maybe && $multi_col) { | ||||
|         // If there were any candidate rows, pick the best one. | ||||
|         asort($maybe); | ||||
|         $row_key = head_key($maybe); | ||||
|       } | ||||
|  | ||||
|       if ($row_key !== null && $multi_row) { | ||||
|         // If there's a candidate row, but adding the sprite to it would make | ||||
|         // the sprite wider than it is tall, create a new row instead. This | ||||
|         // generally keeps the sprite square-ish. | ||||
|         if ($rows[$row_key]['w'] + $s_w > $out_h) { | ||||
|           $row_key = null; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if ($row_key === null) { | ||||
|         // Add a new row. | ||||
|         $rows[] = array( | ||||
|           'w'       => 0, | ||||
|           'h'       => $s_h, | ||||
|           'boxes'   => array(), | ||||
|         ); | ||||
|         $row_key = last_key($rows); | ||||
|         $out_h += $s_h; | ||||
|       } | ||||
|  | ||||
|       // Add the sprite box to the row. | ||||
|       $row = $rows[$row_key]; | ||||
|       $row['w'] += $s_w; | ||||
|       $row['boxes'][] = array($s_w, $s_h); | ||||
|       $rows[$row_key] = $row; | ||||
|  | ||||
|       $out_w = max($row['w'], $out_w); | ||||
|     } | ||||
|  | ||||
|     $images = array(); | ||||
|     foreach ($this->scales as $scale) { | ||||
|       $img = imagecreatetruecolor($out_w * $scale, $out_h * $scale); | ||||
|       imagesavealpha($img, true); | ||||
|       imagefill($img, 0, 0, imagecolorallocatealpha($img, 0, 0, 0, 127)); | ||||
|  | ||||
|       $images[$scale] = $img; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // Put the shorter rows first. At the same height, put the wider rows first. | ||||
|     // This makes the resulting sheet more human-readable. | ||||
|     foreach ($rows as $key => $row) { | ||||
|       $rows[$key]['sort'] = $row['h'] + (1 - ($row['w'] / $out_w)); | ||||
|     } | ||||
|     $rows = isort($rows, 'sort'); | ||||
|  | ||||
|     $pos_x = 0; | ||||
|     $pos_y = 0; | ||||
|     $rules = array(); | ||||
|     foreach ($rows as $row) { | ||||
|       $max_h = 0; | ||||
|       foreach ($row['boxes'] as $box) { | ||||
|         $sprite = array_pop($boxes[$box[0]][$box[1]]); | ||||
|  | ||||
|         foreach ($images as $scale => $img) { | ||||
|           $src = $this->loadSource($sprite, $scale); | ||||
|           imagecopy( | ||||
|             $img, | ||||
|             $src, | ||||
|             $scale * $pos_x,                $scale * $pos_y, | ||||
|             $scale * $sprite->getSourceX(), $scale * $sprite->getSourceY(), | ||||
|             $scale * $sprite->getSourceW(), $scale * $sprite->getSourceH()); | ||||
|         } | ||||
|  | ||||
|         $rule = $sprite->getTargetCSS(); | ||||
|         $cssx = (-$pos_x).'px'; | ||||
|         $cssy = (-$pos_y).'px'; | ||||
|  | ||||
|         $rules[$sprite->getName()] = "{$rule} {\n". | ||||
|           "  background-position: {$cssx} {$cssy};\n}"; | ||||
|  | ||||
|         $pos_x += $sprite->getSourceW() + $margin_w; | ||||
|         $max_h = max($max_h, $sprite->getSourceH()); | ||||
|       } | ||||
|       $pos_x = 0; | ||||
|       $pos_y += $max_h + $margin_h; | ||||
|     } | ||||
|  | ||||
|     // Generate CSS rules in input order. | ||||
|     foreach ($this->sprites as $sprite) { | ||||
|       $css[] = $rules[$sprite->getName()]; | ||||
|     } | ||||
|  | ||||
|     $this->images = $images; | ||||
|     $this->css = implode("\n\n", $css)."\n"; | ||||
|     $this->generated = true; | ||||
|   } | ||||
|  | ||||
|   public function generateImage($path, $scale = 1) { | ||||
|     $this->generate(); | ||||
|     $this->log(pht("Writing sprite '%s'...", $path)); | ||||
|     imagepng($this->images[$scale], $path); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function generateCSS($path) { | ||||
|     $this->generate(); | ||||
|     $this->log(pht("Writing CSS '%s'...", $path)); | ||||
|  | ||||
|     $out = $this->css; | ||||
|     $out = str_replace('{X}', imagesx($this->images[1]), $out); | ||||
|     $out = str_replace('{Y}', imagesy($this->images[1]), $out); | ||||
|  | ||||
|     Filesystem::writeFile($path, $out); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function needsRegeneration(array $manifest) { | ||||
|     return ($this->buildManifest() !== $manifest); | ||||
|   } | ||||
|  | ||||
|   private function buildManifest() { | ||||
|     $output = array(); | ||||
|     foreach ($this->sprites as $sprite) { | ||||
|       $output[$sprite->getName()] = array( | ||||
|         'name' => $sprite->getName(), | ||||
|         'rule' => $sprite->getTargetCSS(), | ||||
|         'hash' => $this->loadSourceHash($sprite), | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     ksort($output); | ||||
|  | ||||
|     $data = array( | ||||
|       'version' => self::MANIFEST_VERSION, | ||||
|       'sprites' => $output, | ||||
|       'scales'  => $this->scales, | ||||
|       'header'  => $this->cssHeader, | ||||
|       'type'    => $this->type, | ||||
|     ); | ||||
|  | ||||
|     return $data; | ||||
|   } | ||||
|  | ||||
|   public function generateManifest($path) { | ||||
|     $data = $this->buildManifest(); | ||||
|  | ||||
|     $json = new PhutilJSON(); | ||||
|     $data = $json->encodeFormatted($data); | ||||
|     Filesystem::writeFile($path, $data); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   private function log($message) { | ||||
|     echo $message."\n"; | ||||
|   } | ||||
|  | ||||
|   private function loadSourceHash(PhutilSprite $sprite) { | ||||
|     $inputs = array(); | ||||
|  | ||||
|     foreach ($this->scales as $scale) { | ||||
|       $file = $sprite->getSourceFile($scale); | ||||
|  | ||||
|       // If two users have a project in different places, like: | ||||
|       // | ||||
|       //    /home/alincoln/project | ||||
|       //    /home/htaft/project | ||||
|       // | ||||
|       // ...we want to ignore the `/home/alincoln` part when hashing the sheet, | ||||
|       // since the sprites don't change when the project directory moves. If | ||||
|       // the base path is set, build the hashes using paths relative to the | ||||
|       // base path. | ||||
|  | ||||
|       $file_key = $file; | ||||
|       if ($this->basePath) { | ||||
|         $file_key = Filesystem::readablePath($file, $this->basePath); | ||||
|       } | ||||
|  | ||||
|       if (empty($this->hashes[$file_key])) { | ||||
|         $this->hashes[$file_key] = md5(Filesystem::readFile($file)); | ||||
|       } | ||||
|  | ||||
|       $inputs[] = $file_key; | ||||
|       $inputs[] = $this->hashes[$file_key]; | ||||
|     } | ||||
|  | ||||
|     $inputs[] = $sprite->getSourceX(); | ||||
|     $inputs[] = $sprite->getSourceY(); | ||||
|     $inputs[] = $sprite->getSourceW(); | ||||
|     $inputs[] = $sprite->getSourceH(); | ||||
|  | ||||
|     return md5(implode(':', $inputs)); | ||||
|   } | ||||
|  | ||||
|   private function loadSource(PhutilSprite $sprite, $scale) { | ||||
|     $file = $sprite->getSourceFile($scale); | ||||
|     if (empty($this->sources[$file])) { | ||||
|       $data = Filesystem::readFile($file); | ||||
|       $image = imagecreatefromstring($data); | ||||
|       $this->sources[$file] = array( | ||||
|         'image' => $image, | ||||
|         'x'     => imagesx($image), | ||||
|         'y'     => imagesy($image), | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     $s_w = $sprite->getSourceW() * $scale; | ||||
|     $i_w = $this->sources[$file]['x']; | ||||
|     if ($s_w > $i_w) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           "Sprite source for '%s' is too small (expected width %d, found %d).", | ||||
|           $file, | ||||
|           $s_w, | ||||
|           $i_w)); | ||||
|     } | ||||
|  | ||||
|     $s_h = $sprite->getSourceH() * $scale; | ||||
|     $i_h = $this->sources[$file]['y']; | ||||
|     if ($s_h > $i_h) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           "Sprite source for '%s' is too small (expected height %d, found %d).", | ||||
|           $file, | ||||
|           $s_h, | ||||
|           $i_h)); | ||||
|     } | ||||
|  | ||||
|     return $this->sources[$file]['image']; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,9 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class AphrontScopedUnguardedWriteCapability extends Phobject { | ||||
|  | ||||
|   public function __destruct() { | ||||
|     AphrontWriteGuard::endUnguardedWrites(); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,267 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Guard writes against CSRF. The Aphront structure takes care of most of this | ||||
|  * for you, you just need to call: | ||||
|  * | ||||
|  *    AphrontWriteGuard::willWrite(); | ||||
|  * | ||||
|  * ...before executing a write against any new kind of storage engine. MySQL | ||||
|  * databases and the default file storage engines are already covered, but if | ||||
|  * you introduce new types of datastores make sure their writes are guarded. If | ||||
|  * you don't guard writes and make a mistake doing CSRF checks in a controller, | ||||
|  * a CSRF vulnerability can escape undetected. | ||||
|  * | ||||
|  * If you need to execute writes on a page which doesn't have CSRF tokens (for | ||||
|  * example, because you need to do logging), you can temporarily disable the | ||||
|  * write guard by calling: | ||||
|  * | ||||
|  *    AphrontWriteGuard::beginUnguardedWrites(); | ||||
|  *    do_logging_write(); | ||||
|  *    AphrontWriteGuard::endUnguardedWrites(); | ||||
|  * | ||||
|  * This is dangerous, because it disables the backup layer of CSRF protection | ||||
|  * this class provides. You should need this only very, very rarely. | ||||
|  * | ||||
|  * @task protect  Protecting Writes | ||||
|  * @task disable  Disabling Protection | ||||
|  * @task manage   Managing Write Guards | ||||
|  * @task internal Internals | ||||
|  */ | ||||
| final class AphrontWriteGuard extends Phobject { | ||||
|  | ||||
|   private static $instance; | ||||
|   private static $allowUnguardedWrites = false; | ||||
|  | ||||
|   private $callback; | ||||
|   private $allowDepth = 0; | ||||
|  | ||||
|  | ||||
| /* -(  Managing Write Guards  )---------------------------------------------- */ | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Construct a new write guard for a request. Only one write guard may be | ||||
|    * active at a time. You must explicitly call @{method:dispose} when you are | ||||
|    * done with a write guard: | ||||
|    * | ||||
|    *    $guard = new AphrontWriteGuard($callback); | ||||
|    *    // ... | ||||
|    *    $guard->dispose(); | ||||
|    * | ||||
|    * Normally, you do not need to manage guards yourself -- the Aphront stack | ||||
|    * handles it for you. | ||||
|    * | ||||
|    * This class accepts a callback, which will be invoked when a write is | ||||
|    * attempted. The callback should validate the presence of a CSRF token in | ||||
|    * the request, or abort the request (e.g., by throwing an exception) if a | ||||
|    * valid token isn't present. | ||||
|    * | ||||
|    * @param   callable CSRF callback. | ||||
|    * @return  this | ||||
|    * @task    manage | ||||
|    */ | ||||
|   public function __construct($callback) { | ||||
|     if (self::$instance) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'An %s already exists. Dispose of the previous guard '. | ||||
|           'before creating a new one.', | ||||
|           __CLASS__)); | ||||
|     } | ||||
|     if (self::$allowUnguardedWrites) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'An %s is being created in a context which permits '. | ||||
|           'unguarded writes unconditionally. This is not allowed and '. | ||||
|           'indicates a serious error.', | ||||
|           __CLASS__)); | ||||
|     } | ||||
|     $this->callback = $callback; | ||||
|     self::$instance = $this; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Dispose of the active write guard. You must call this method when you are | ||||
|    * done with a write guard. You do not normally need to call this yourself. | ||||
|    * | ||||
|    * @return void | ||||
|    * @task manage | ||||
|    */ | ||||
|   public function dispose() { | ||||
|     if (!self::$instance) { | ||||
|       throw new Exception(pht( | ||||
|         'Attempting to dispose of write guard, but no write guard is active!')); | ||||
|     } | ||||
|  | ||||
|     if ($this->allowDepth > 0) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Imbalanced %s: more %s calls than %s calls.', | ||||
|           __CLASS__, | ||||
|           'beginUnguardedWrites()', | ||||
|           'endUnguardedWrites()')); | ||||
|     } | ||||
|     self::$instance = null; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Determine if there is an active write guard. | ||||
|    * | ||||
|    * @return bool | ||||
|    * @task manage | ||||
|    */ | ||||
|   public static function isGuardActive() { | ||||
|     return (bool)self::$instance; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Return on instance of AphrontWriteGuard if it's active, or null | ||||
|    * | ||||
|    * @return AphrontWriteGuard|null | ||||
|    */ | ||||
|   public static function getInstance() { | ||||
|     return self::$instance; | ||||
|   } | ||||
|  | ||||
|  | ||||
| /* -(  Protecting Writes  )-------------------------------------------------- */ | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Declare intention to perform a write, validating that writes are allowed. | ||||
|    * You should call this method before executing a write whenever you implement | ||||
|    * a new storage engine where information can be permanently kept. | ||||
|    * | ||||
|    * Writes are permitted if: | ||||
|    * | ||||
|    *   - The request has valid CSRF tokens. | ||||
|    *   - Unguarded writes have been temporarily enabled by a call to | ||||
|    *     @{method:beginUnguardedWrites}. | ||||
|    *   - All write guarding has been disabled with | ||||
|    *     @{method:allowDangerousUnguardedWrites}. | ||||
|    * | ||||
|    * If none of these conditions are true, this method will throw and prevent | ||||
|    * the write. | ||||
|    * | ||||
|    * @return void | ||||
|    * @task protect | ||||
|    */ | ||||
|   public static function willWrite() { | ||||
|     if (!self::$instance) { | ||||
|       if (!self::$allowUnguardedWrites) { | ||||
|         throw new Exception( | ||||
|           pht( | ||||
|             'Unguarded write! There must be an active %s to perform writes.', | ||||
|             __CLASS__)); | ||||
|       } else { | ||||
|         // Unguarded writes are being allowed unconditionally. | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $instance = self::$instance; | ||||
|     if ($instance->allowDepth == 0) { | ||||
|       call_user_func($instance->callback); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
| /* -(  Disabling Write Protection  )----------------------------------------- */ | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Enter a scope which permits unguarded writes. This works like | ||||
|    * @{method:beginUnguardedWrites} but returns an object which will end | ||||
|    * the unguarded write scope when its __destruct() method is called. This | ||||
|    * is useful to more easily handle exceptions correctly in unguarded write | ||||
|    * blocks: | ||||
|    * | ||||
|    *   // Restores the guard even if do_logging() throws. | ||||
|    *   function unguarded_scope() { | ||||
|    *     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | ||||
|    *     do_logging(); | ||||
|    *   } | ||||
|    * | ||||
|    * @return AphrontScopedUnguardedWriteCapability Object which ends unguarded | ||||
|    *            writes when it leaves scope. | ||||
|    * @task disable | ||||
|    */ | ||||
|   public static function beginScopedUnguardedWrites() { | ||||
|     self::beginUnguardedWrites(); | ||||
|     return new AphrontScopedUnguardedWriteCapability(); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Begin a block which permits unguarded writes. You should use this very | ||||
|    * sparingly, and only for things like logging where CSRF is not a concern. | ||||
|    * | ||||
|    * You must pair every call to @{method:beginUnguardedWrites} with a call to | ||||
|    * @{method:endUnguardedWrites}: | ||||
|    * | ||||
|    *   AphrontWriteGuard::beginUnguardedWrites(); | ||||
|    *   do_logging(); | ||||
|    *   AphrontWriteGuard::endUnguardedWrites(); | ||||
|    * | ||||
|    * @return void | ||||
|    * @task disable | ||||
|    */ | ||||
|   public static function beginUnguardedWrites() { | ||||
|     if (!self::$instance) { | ||||
|       return; | ||||
|     } | ||||
|     self::$instance->allowDepth++; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Declare that you have finished performing unguarded writes. You must | ||||
|    * call this exactly once for each call to @{method:beginUnguardedWrites}. | ||||
|    * | ||||
|    * @return void | ||||
|    * @task disable | ||||
|    */ | ||||
|   public static function endUnguardedWrites() { | ||||
|     if (!self::$instance) { | ||||
|       return; | ||||
|     } | ||||
|     if (self::$instance->allowDepth <= 0) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Imbalanced %s: more %s calls than %s calls.', | ||||
|           __CLASS__, | ||||
|           'endUnguardedWrites()', | ||||
|           'beginUnguardedWrites()')); | ||||
|     } | ||||
|     self::$instance->allowDepth--; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Allow execution of unguarded writes. This is ONLY appropriate for use in | ||||
|    * script contexts or other contexts where you are guaranteed to never be | ||||
|    * vulnerable to CSRF concerns. Calling this method is EXTREMELY DANGEROUS | ||||
|    * if you do not understand the consequences. | ||||
|    * | ||||
|    * If you need to perform unguarded writes on an otherwise guarded workflow | ||||
|    * which is vulnerable to CSRF, use @{method:beginUnguardedWrites}. | ||||
|    * | ||||
|    * @return void | ||||
|    * @task disable | ||||
|    */ | ||||
|   public static function allowDangerousUnguardedWrites($allow) { | ||||
|     if (self::$instance) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'You can not unconditionally disable %s by calling %s while a write '. | ||||
|           'guard is active. Use %s to temporarily allow unguarded writes.', | ||||
|           __CLASS__, | ||||
|           __FUNCTION__.'()', | ||||
|           'beginUnguardedWrites()')); | ||||
|     } | ||||
|     self::$allowUnguardedWrites = true; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -10,15 +10,13 @@ final class AlmanacConsoleController extends AlmanacController { | ||||
|     $viewer = $request->getViewer(); | ||||
|  | ||||
|     $menu = id(new PHUIObjectItemListView()) | ||||
|       ->setViewer($viewer) | ||||
|       ->setBig(true); | ||||
|       ->setUser($viewer); | ||||
|  | ||||
|     $menu->addItem( | ||||
|       id(new PHUIObjectItemView()) | ||||
|         ->setHeader(pht('Devices')) | ||||
|         ->setHref($this->getApplicationURI('device/')) | ||||
|         ->setImageIcon('fa-server') | ||||
|         ->setClickable(true) | ||||
|         ->addAttribute( | ||||
|           pht( | ||||
|             'Create an inventory of physical and virtual hosts and '. | ||||
| @@ -29,7 +27,6 @@ final class AlmanacConsoleController extends AlmanacController { | ||||
|         ->setHeader(pht('Services')) | ||||
|         ->setHref($this->getApplicationURI('service/')) | ||||
|         ->setImageIcon('fa-plug') | ||||
|         ->setClickable(true) | ||||
|         ->addAttribute( | ||||
|           pht( | ||||
|             'Create and update services, and map them to interfaces on '. | ||||
| @@ -40,7 +37,6 @@ final class AlmanacConsoleController extends AlmanacController { | ||||
|         ->setHeader(pht('Networks')) | ||||
|         ->setHref($this->getApplicationURI('network/')) | ||||
|         ->setImageIcon('fa-globe') | ||||
|         ->setClickable(true) | ||||
|         ->addAttribute( | ||||
|           pht( | ||||
|             'Manage public and private networks.'))); | ||||
| @@ -50,7 +46,6 @@ final class AlmanacConsoleController extends AlmanacController { | ||||
|         ->setHeader(pht('Namespaces')) | ||||
|         ->setHref($this->getApplicationURI('namespace/')) | ||||
|         ->setImageIcon('fa-asterisk') | ||||
|         ->setClickable(true) | ||||
|         ->addAttribute( | ||||
|           pht('Control who can create new named services and devices.'))); | ||||
|  | ||||
| @@ -62,7 +57,6 @@ final class AlmanacConsoleController extends AlmanacController { | ||||
|         ->setHeader(pht('Documentation')) | ||||
|         ->setHref($docs_uri) | ||||
|         ->setImageIcon('fa-book') | ||||
|         ->setClickable(true) | ||||
|         ->addAttribute(pht('Browse documentation for Almanac.'))); | ||||
|  | ||||
|     $crumbs = $this->buildApplicationCrumbs(); | ||||
| @@ -70,20 +64,23 @@ final class AlmanacConsoleController extends AlmanacController { | ||||
|     $crumbs->setBorder(true); | ||||
|  | ||||
|     $box = id(new PHUIObjectBoxView()) | ||||
|       ->setHeaderText(pht('Almanac Console')) | ||||
|       ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) | ||||
|       ->setObjectList($menu); | ||||
|  | ||||
|     $launcher_view = id(new PHUILauncherView()) | ||||
|       ->appendChild($box); | ||||
|     $header = id(new PHUIHeaderView()) | ||||
|       ->setHeader(pht('Almanac Console')) | ||||
|       ->setHeaderIcon('fa-server'); | ||||
|  | ||||
|     $view = id(new PHUITwoColumnView()) | ||||
|       ->setFooter($launcher_view); | ||||
|       ->setHeader($header) | ||||
|       ->setFooter(array( | ||||
|         $box, | ||||
|       )); | ||||
|  | ||||
|     return $this->newPage() | ||||
|       ->setTitle(pht('Almanac Console')) | ||||
|       ->setCrumbs($crumbs) | ||||
|       ->appendChild($view); | ||||
|  | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -57,6 +57,15 @@ final class AlmanacKeys extends Phobject { | ||||
|   } | ||||
|  | ||||
|   public static function getClusterSSHUser() { | ||||
|     // NOTE: When instancing, we currently use the SSH username to figure out | ||||
|     // which instance you are connecting to. We can't use the host name because | ||||
|     // we have no way to tell which host you think you're reaching: the SSH | ||||
|     // protocol does not have a mechanism like a "Host" header. | ||||
|     $username = PhabricatorEnv::getEnvConfig('cluster.instance'); | ||||
|     if (strlen($username)) { | ||||
|       return $username; | ||||
|     } | ||||
|  | ||||
|     $username = PhabricatorEnv::getEnvConfig('diffusion.ssh-user'); | ||||
|     if (strlen($username)) { | ||||
|       return $username; | ||||
|   | ||||
| @@ -550,18 +550,11 @@ abstract class PhabricatorAphlictManagementWorkflow | ||||
|   } | ||||
|  | ||||
|   private function getStartCommand(array $server_argv) { | ||||
|     $launch_argv = array(); | ||||
|  | ||||
|     if ($this->debug) { | ||||
|       $launch_argv[] = '--debug=1'; | ||||
|     } | ||||
|  | ||||
|     return csprintf( | ||||
|       '%R %Ls -- %s %Ls %Ls', | ||||
|       '%R %Ls -- %s %Ls', | ||||
|       $this->getNodeBinary(), | ||||
|       $this->getNodeArgv(), | ||||
|       $this->getAphlictScriptPath(), | ||||
|       $launch_argv, | ||||
|       $server_argv); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -105,14 +105,6 @@ final class PhabricatorAuditEditor | ||||
|  | ||||
|     switch ($xaction->getTransactionType()) { | ||||
|       case PhabricatorAuditActionConstants::INLINE: | ||||
|         $comment = $xaction->getComment(); | ||||
|  | ||||
|         $comment->setAttribute('editing', false); | ||||
|  | ||||
|         PhabricatorVersionedDraft::purgeDrafts( | ||||
|           $comment->getPHID(), | ||||
|           $this->getActingAsPHID()); | ||||
|         return; | ||||
|       case PhabricatorAuditTransaction::TYPE_COMMIT: | ||||
|         return; | ||||
|     } | ||||
| @@ -240,22 +232,14 @@ final class PhabricatorAuditEditor | ||||
|     PhabricatorLiskDAO $object, | ||||
|     PhabricatorApplicationTransaction $xaction) { | ||||
|  | ||||
|     $auditors_type = DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE; | ||||
|  | ||||
|     $xactions = parent::expandTransaction($object, $xaction); | ||||
|  | ||||
|     switch ($xaction->getTransactionType()) { | ||||
|       case PhabricatorAuditTransaction::TYPE_COMMIT: | ||||
|         $phids = $this->getAuditRequestTransactionPHIDsFromCommitMessage( | ||||
|         $request = $this->createAuditRequestTransactionFromCommitMessage( | ||||
|           $object); | ||||
|         if ($phids) { | ||||
|           $xactions[] = $object->getApplicationTransactionTemplate() | ||||
|             ->setTransactionType($auditors_type) | ||||
|             ->setNewValue( | ||||
|               array( | ||||
|                 '+' => array_fuse($phids), | ||||
|               )); | ||||
|           $this->addUnmentionablePHIDs($phids); | ||||
|         if ($request) { | ||||
|           $xactions[] = $request; | ||||
|           $this->addUnmentionablePHIDs($request->getNewValue()); | ||||
|         } | ||||
|         break; | ||||
|       default: | ||||
| @@ -284,7 +268,7 @@ final class PhabricatorAuditEditor | ||||
|     return $xactions; | ||||
|   } | ||||
|  | ||||
|   private function getAuditRequestTransactionPHIDsFromCommitMessage( | ||||
|   private function createAuditRequestTransactionFromCommitMessage( | ||||
|     PhabricatorRepositoryCommit $commit) { | ||||
|  | ||||
|     $actor = $this->getActor(); | ||||
| @@ -313,7 +297,12 @@ final class PhabricatorAuditEditor | ||||
|       return array(); | ||||
|     } | ||||
|  | ||||
|     return $phids; | ||||
|     return $commit->getApplicationTransactionTemplate() | ||||
|       ->setTransactionType(DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE) | ||||
|       ->setNewValue( | ||||
|         array( | ||||
|           '+' => array_fuse($phids), | ||||
|         )); | ||||
|   } | ||||
|  | ||||
|   protected function sortTransactions(array $xactions) { | ||||
| @@ -416,31 +405,6 @@ final class PhabricatorAuditEditor | ||||
|       $phid_map[] = $reverted_phids; | ||||
|     } | ||||
|  | ||||
|     // See T13463. Copy "related task" edges from the associated revision, if | ||||
|     // one exists. | ||||
|  | ||||
|     $revision = DiffusionCommitRevisionQuery::loadRevisionForCommit( | ||||
|       $actor, | ||||
|       $object); | ||||
|     if ($revision) { | ||||
|       $task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( | ||||
|         $revision->getPHID(), | ||||
|         DifferentialRevisionHasTaskEdgeType::EDGECONST); | ||||
|       $task_phids = array_fuse($task_phids); | ||||
|  | ||||
|       if ($task_phids) { | ||||
|         $related_edge = DiffusionCommitHasTaskEdgeType::EDGECONST; | ||||
|         $result[] = id(new PhabricatorAuditTransaction()) | ||||
|           ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) | ||||
|           ->setMetadataValue('edge:type', $related_edge) | ||||
|           ->setNewValue(array('+' => $task_phids)); | ||||
|       } | ||||
|  | ||||
|       // Mark these objects as unmentionable, since the explicit relationship | ||||
|       // is stronger and any mentions are redundant. | ||||
|       $phid_map[] = $task_phids; | ||||
|     } | ||||
|  | ||||
|     $phid_map = array_mergev($phid_map); | ||||
|     $this->addUnmentionablePHIDs($phid_map); | ||||
|  | ||||
|   | ||||
| @@ -1,75 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class DiffusionInternalCommitSearchEngine | ||||
|   extends PhabricatorApplicationSearchEngine { | ||||
|  | ||||
|   public function getResultTypeDescription() { | ||||
|     return pht('Diffusion Raw Commits'); | ||||
|   } | ||||
|  | ||||
|   public function getApplicationClassName() { | ||||
|     return 'PhabricatorDiffusionApplication'; | ||||
|   } | ||||
|  | ||||
|   public function newQuery() { | ||||
|     return new DiffusionCommitQuery(); | ||||
|   } | ||||
|  | ||||
|   protected function buildQueryFromParameters(array $map) { | ||||
|     $query = $this->newQuery(); | ||||
|  | ||||
|     if ($map['repositoryPHIDs']) { | ||||
|       $query->withRepositoryPHIDs($map['repositoryPHIDs']); | ||||
|     } | ||||
|  | ||||
|     return $query; | ||||
|   } | ||||
|  | ||||
|   protected function buildCustomSearchFields() { | ||||
|     return array( | ||||
|       id(new PhabricatorSearchDatasourceField()) | ||||
|         ->setLabel(pht('Repositories')) | ||||
|         ->setKey('repositoryPHIDs') | ||||
|         ->setDatasource(new DiffusionRepositoryFunctionDatasource()) | ||||
|         ->setDescription(pht('Find commits in particular repositories.')), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   protected function getURI($path) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   protected function renderResultList( | ||||
|     array $commits, | ||||
|     PhabricatorSavedQuery $query, | ||||
|     array $handles) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   protected function getObjectWireFieldsForConduit( | ||||
|     $object, | ||||
|     array $field_extensions, | ||||
|     array $extension_data) { | ||||
|  | ||||
|     $commit = $object; | ||||
|     $viewer = $this->requireViewer(); | ||||
|  | ||||
|     $repository = $commit->getRepository(); | ||||
|     $identifier = $commit->getCommitIdentifier(); | ||||
|  | ||||
|     id(new DiffusionRepositoryClusterEngine()) | ||||
|       ->setViewer($viewer) | ||||
|       ->setRepository($repository) | ||||
|       ->synchronizeWorkingCopyBeforeRead(); | ||||
|  | ||||
|     $ref = id(new DiffusionLowLevelCommitQuery()) | ||||
|       ->setRepository($repository) | ||||
|       ->withIdentifier($identifier) | ||||
|       ->execute(); | ||||
|  | ||||
|     return array( | ||||
|       'ref' => $ref->newDictionary(), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -221,9 +221,9 @@ final class PhabricatorCommitSearchEngine | ||||
|  | ||||
|     $bucket = $this->getResultBucket($query); | ||||
|  | ||||
|     $template = id(new DiffusionCommitGraphView()) | ||||
|     $template = id(new PhabricatorAuditListView()) | ||||
|       ->setViewer($viewer) | ||||
|       ->setShowAuditors(true); | ||||
|       ->setShowDrafts(true); | ||||
|  | ||||
|     $views = array(); | ||||
|     if ($bucket) { | ||||
| @@ -235,31 +235,37 @@ final class PhabricatorCommitSearchEngine | ||||
|         foreach ($groups as $group) { | ||||
|           // Don't show groups in Dashboard Panels | ||||
|           if ($group->getObjects() || !$this->isPanelContext()) { | ||||
|             $item_list = id(clone $template) | ||||
|               ->setCommits($group->getObjects()) | ||||
|               ->newObjectItemListView(); | ||||
|  | ||||
|             $views[] = $item_list | ||||
|             $views[] = id(clone $template) | ||||
|               ->setHeader($group->getName()) | ||||
|               ->setNoDataString($group->getNoDataString()); | ||||
|               ->setNoDataString($group->getNoDataString()) | ||||
|               ->setCommits($group->getObjects()); | ||||
|           } | ||||
|         } | ||||
|       } catch (Exception $ex) { | ||||
|         $this->addError($ex->getMessage()); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (!$views) { | ||||
|       $item_list = id(clone $template) | ||||
|     } else { | ||||
|       $views[] = id(clone $template) | ||||
|         ->setCommits($commits) | ||||
|         ->newObjectItemListView(); | ||||
|  | ||||
|       $views[] = $item_list | ||||
|         ->setNoDataString(pht('No commits found.')); | ||||
|     } | ||||
|  | ||||
|     return id(new PhabricatorApplicationSearchResultView()) | ||||
|       ->setContent($views); | ||||
|     if (!$views) { | ||||
|       $views[] = id(new PhabricatorAuditListView()) | ||||
|         ->setViewer($viewer) | ||||
|         ->setNoDataString(pht('No commits found.')); | ||||
|     } | ||||
|  | ||||
|     if (count($views) == 1) { | ||||
|       $list = head($views)->buildList(); | ||||
|     } else { | ||||
|       $list = $views; | ||||
|     } | ||||
|  | ||||
|     $result = new PhabricatorApplicationSearchResultView(); | ||||
|     $result->setContent($list); | ||||
|  | ||||
|     return $result; | ||||
|   } | ||||
|  | ||||
|   protected function getNewUserBody() { | ||||
|   | ||||
| @@ -1,16 +1,27 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorAuditInlineComment | ||||
|   extends PhabricatorInlineComment { | ||||
|   extends Phobject | ||||
|   implements PhabricatorInlineCommentInterface { | ||||
|  | ||||
|   protected function newStorageObject() { | ||||
|     return new PhabricatorAuditTransactionComment(); | ||||
|   private $proxy; | ||||
|   private $syntheticAuthor; | ||||
|   private $isGhost; | ||||
|  | ||||
|   public function __construct() { | ||||
|     $this->proxy = new PhabricatorAuditTransactionComment(); | ||||
|   } | ||||
|  | ||||
|   public function getControllerURI() { | ||||
|     return urisprintf( | ||||
|       '/diffusion/inline/edit/%s/', | ||||
|       $this->getCommitPHID()); | ||||
|   public function __clone() { | ||||
|     $this->proxy = clone $this->proxy; | ||||
|   } | ||||
|  | ||||
|   public function getTransactionPHID() { | ||||
|     return $this->proxy->getTransactionPHID(); | ||||
|   } | ||||
|  | ||||
|   public function getTransactionComment() { | ||||
|     return $this->proxy; | ||||
|   } | ||||
|  | ||||
|   public function supportsHiding() { | ||||
| @@ -25,40 +36,246 @@ final class PhabricatorAuditInlineComment | ||||
|     $content_source = PhabricatorContentSource::newForSource( | ||||
|       PhabricatorOldWorldContentSource::SOURCECONST); | ||||
|  | ||||
|     $this->getStorageObject() | ||||
|     $this->proxy | ||||
|       ->setViewPolicy('public') | ||||
|       ->setEditPolicy($this->getAuthorPHID()) | ||||
|       ->setContentSource($content_source) | ||||
|       ->setCommentVersion(1); | ||||
|  | ||||
|     return $this->getStorageObject(); | ||||
|     return $this->proxy; | ||||
|   } | ||||
|  | ||||
|   public static function loadID($id) { | ||||
|     $inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere( | ||||
|       'id = %d', | ||||
|       $id); | ||||
|     if (!$inlines) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     return head(self::buildProxies($inlines)); | ||||
|   } | ||||
|  | ||||
|   public static function loadPHID($phid) { | ||||
|     $inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere( | ||||
|       'phid = %s', | ||||
|       $phid); | ||||
|     if (!$inlines) { | ||||
|       return null; | ||||
|     } | ||||
|     return head(self::buildProxies($inlines)); | ||||
|   } | ||||
|  | ||||
|   public static function loadDraftComments( | ||||
|     PhabricatorUser $viewer, | ||||
|     $commit_phid, | ||||
|     $raw = false) { | ||||
|  | ||||
|     $inlines = id(new DiffusionDiffInlineCommentQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->withAuthorPHIDs(array($viewer->getPHID())) | ||||
|       ->withCommitPHIDs(array($commit_phid)) | ||||
|       ->withHasTransaction(false) | ||||
|       ->withHasPath(true) | ||||
|       ->withIsDeleted(false) | ||||
|       ->needReplyToComments(true) | ||||
|       ->execute(); | ||||
|  | ||||
|     if ($raw) { | ||||
|       return $inlines; | ||||
|     } | ||||
|  | ||||
|     return self::buildProxies($inlines); | ||||
|   } | ||||
|  | ||||
|   public static function loadPublishedComments( | ||||
|     PhabricatorUser $viewer, | ||||
|     $commit_phid) { | ||||
|  | ||||
|     $inlines = id(new DiffusionDiffInlineCommentQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->withCommitPHIDs(array($commit_phid)) | ||||
|       ->withHasTransaction(true) | ||||
|       ->withHasPath(true) | ||||
|       ->execute(); | ||||
|  | ||||
|     return self::buildProxies($inlines); | ||||
|   } | ||||
|  | ||||
|   public static function loadDraftAndPublishedComments( | ||||
|     PhabricatorUser $viewer, | ||||
|     $commit_phid, | ||||
|     $path_id = null) { | ||||
|  | ||||
|     if ($path_id === null) { | ||||
|       $inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere( | ||||
|         'commitPHID = %s AND (transactionPHID IS NOT NULL OR authorPHID = %s) | ||||
|           AND pathID IS NOT NULL', | ||||
|         $commit_phid, | ||||
|         $viewer->getPHID()); | ||||
|     } else { | ||||
|       $inlines = id(new PhabricatorAuditTransactionComment())->loadAllWhere( | ||||
|         'commitPHID = %s AND pathID = %d AND | ||||
|           ((authorPHID = %s AND isDeleted = 0) OR transactionPHID IS NOT NULL)', | ||||
|         $commit_phid, | ||||
|         $path_id, | ||||
|         $viewer->getPHID()); | ||||
|     } | ||||
|  | ||||
|     return self::buildProxies($inlines); | ||||
|   } | ||||
|  | ||||
|   private static function buildProxies(array $inlines) { | ||||
|     $results = array(); | ||||
|     foreach ($inlines as $key => $inline) { | ||||
|       $results[$key] = self::newFromModernComment( | ||||
|         $inline); | ||||
|     } | ||||
|     return $results; | ||||
|   } | ||||
|  | ||||
|   public function setSyntheticAuthor($synthetic_author) { | ||||
|     $this->syntheticAuthor = $synthetic_author; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getSyntheticAuthor() { | ||||
|     return $this->syntheticAuthor; | ||||
|   } | ||||
|  | ||||
|   public function openTransaction() { | ||||
|     $this->proxy->openTransaction(); | ||||
|   } | ||||
|  | ||||
|   public function saveTransaction() { | ||||
|     $this->proxy->saveTransaction(); | ||||
|   } | ||||
|  | ||||
|   public function save() { | ||||
|     $this->getTransactionCommentForSave()->save(); | ||||
|  | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function delete() { | ||||
|     $this->proxy->delete(); | ||||
|  | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getID() { | ||||
|     return $this->proxy->getID(); | ||||
|   } | ||||
|  | ||||
|   public function getPHID() { | ||||
|     return $this->proxy->getPHID(); | ||||
|   } | ||||
|  | ||||
|   public static function newFromModernComment( | ||||
|     PhabricatorAuditTransactionComment $comment) { | ||||
|  | ||||
|     $obj = new PhabricatorAuditInlineComment(); | ||||
|     $obj->setStorageObject($comment); | ||||
|     $obj->proxy = $comment; | ||||
|  | ||||
|     return $obj; | ||||
|   } | ||||
|  | ||||
|   public function isCompatible(PhabricatorInlineCommentInterface $comment) { | ||||
|     return | ||||
|       ($this->getAuthorPHID() === $comment->getAuthorPHID()) && | ||||
|       ($this->getSyntheticAuthor() === $comment->getSyntheticAuthor()) && | ||||
|       ($this->getContent() === $comment->getContent()); | ||||
|   } | ||||
|  | ||||
|   public function setContent($content) { | ||||
|     $this->proxy->setContent($content); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getContent() { | ||||
|     return $this->proxy->getContent(); | ||||
|   } | ||||
|  | ||||
|   public function isDraft() { | ||||
|     return !$this->proxy->getTransactionPHID(); | ||||
|   } | ||||
|  | ||||
|   public function setPathID($id) { | ||||
|     $this->getStorageObject()->setPathID($id); | ||||
|     $this->proxy->setPathID($id); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getPathID() { | ||||
|     return $this->getStorageObject()->getPathID(); | ||||
|     return $this->proxy->getPathID(); | ||||
|   } | ||||
|  | ||||
|   public function setIsNewFile($is_new) { | ||||
|     $this->proxy->setIsNewFile($is_new); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getIsNewFile() { | ||||
|     return $this->proxy->getIsNewFile(); | ||||
|   } | ||||
|  | ||||
|   public function setLineNumber($number) { | ||||
|     $this->proxy->setLineNumber($number); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getLineNumber() { | ||||
|     return $this->proxy->getLineNumber(); | ||||
|   } | ||||
|  | ||||
|   public function setLineLength($length) { | ||||
|     $this->proxy->setLineLength($length); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getLineLength() { | ||||
|     return $this->proxy->getLineLength(); | ||||
|   } | ||||
|  | ||||
|   public function setCache($cache) { | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getCache() { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   public function setAuthorPHID($phid) { | ||||
|     $this->proxy->setAuthorPHID($phid); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getAuthorPHID() { | ||||
|     return $this->proxy->getAuthorPHID(); | ||||
|   } | ||||
|  | ||||
|   public function setCommitPHID($commit_phid) { | ||||
|     $this->getStorageObject()->setCommitPHID($commit_phid); | ||||
|     $this->proxy->setCommitPHID($commit_phid); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getCommitPHID() { | ||||
|     return $this->getStorageObject()->getCommitPHID(); | ||||
|     return $this->proxy->getCommitPHID(); | ||||
|   } | ||||
|  | ||||
|   // When setting a comment ID, we also generate a phantom transaction PHID for | ||||
|   // the future transaction. | ||||
|  | ||||
|   public function setAuditCommentID($id) { | ||||
|     $this->proxy->setLegacyCommentID($id); | ||||
|     $this->proxy->setTransactionPHID( | ||||
|       PhabricatorPHID::generateNewPHID( | ||||
|         PhabricatorApplicationTransactionTransactionPHIDType::TYPECONST, | ||||
|         PhabricatorRepositoryCommitPHIDType::TYPECONST)); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getAuditCommentID() { | ||||
|     return $this->proxy->getLegacyCommentID(); | ||||
|   } | ||||
|  | ||||
|   public function setChangesetID($id) { | ||||
| @@ -69,4 +286,82 @@ final class PhabricatorAuditInlineComment | ||||
|     return $this->getPathID(); | ||||
|   } | ||||
|  | ||||
|   public function setReplyToCommentPHID($phid) { | ||||
|     $this->proxy->setReplyToCommentPHID($phid); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getReplyToCommentPHID() { | ||||
|     return $this->proxy->getReplyToCommentPHID(); | ||||
|   } | ||||
|  | ||||
|   public function setHasReplies($has_replies) { | ||||
|     $this->proxy->setHasReplies($has_replies); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getHasReplies() { | ||||
|     return $this->proxy->getHasReplies(); | ||||
|   } | ||||
|  | ||||
|   public function setIsDeleted($is_deleted) { | ||||
|     $this->proxy->setIsDeleted($is_deleted); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getIsDeleted() { | ||||
|     return $this->proxy->getIsDeleted(); | ||||
|   } | ||||
|  | ||||
|   public function setFixedState($state) { | ||||
|     $this->proxy->setFixedState($state); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getFixedState() { | ||||
|     return $this->proxy->getFixedState(); | ||||
|   } | ||||
|  | ||||
|   public function setIsGhost($is_ghost) { | ||||
|     $this->isGhost = $is_ghost; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getIsGhost() { | ||||
|     return $this->isGhost; | ||||
|   } | ||||
|  | ||||
|   public function getDateModified() { | ||||
|     return $this->proxy->getDateModified(); | ||||
|   } | ||||
|  | ||||
|   public function getDateCreated() { | ||||
|     return $this->proxy->getDateCreated(); | ||||
|   } | ||||
|  | ||||
|  | ||||
| /* -(  PhabricatorMarkupInterface Implementation  )-------------------------- */ | ||||
|  | ||||
|  | ||||
|   public function getMarkupFieldKey($field) { | ||||
|     return 'AI:'.$this->getID(); | ||||
|   } | ||||
|  | ||||
|   public function newMarkupEngine($field) { | ||||
|     return PhabricatorMarkupEngine::newDifferentialMarkupEngine(); | ||||
|   } | ||||
|  | ||||
|   public function getMarkupText($field) { | ||||
|     return $this->getContent(); | ||||
|   } | ||||
|  | ||||
|   public function didMarkupText($field, $output, PhutilMarkupEngine $engine) { | ||||
|     return $output; | ||||
|   } | ||||
|  | ||||
|   public function shouldUseMarkupCache($field) { | ||||
|     // Only cache submitted comments. | ||||
|     return ($this->getID() && $this->getAuditCommentID()); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorAuditTransactionComment | ||||
|   extends PhabricatorApplicationTransactionComment | ||||
|   implements | ||||
|     PhabricatorInlineCommentInterface { | ||||
|   extends PhabricatorApplicationTransactionComment { | ||||
|  | ||||
|   protected $commitPHID; | ||||
|   protected $pathID; | ||||
| @@ -14,10 +12,8 @@ final class PhabricatorAuditTransactionComment | ||||
|   protected $hasReplies = 0; | ||||
|   protected $replyToCommentPHID; | ||||
|   protected $legacyCommentID; | ||||
|   protected $attributes = array(); | ||||
|  | ||||
|   private $replyToComment = self::ATTACHABLE; | ||||
|   private $inlineContext = self::ATTACHABLE; | ||||
|  | ||||
|   public function getApplicationTransactionObject() { | ||||
|     return new PhabricatorAuditTransaction(); | ||||
| @@ -58,10 +54,6 @@ final class PhabricatorAuditTransactionComment | ||||
|       ), | ||||
|     ) + $config[self::CONFIG_KEY_SCHEMA]; | ||||
|  | ||||
|     $config[self::CONFIG_SERIALIZATION] = array( | ||||
|       'attributes' => self::SERIALIZATION_JSON, | ||||
|     ) + idx($config, self::CONFIG_SERIALIZATION, array()); | ||||
|  | ||||
|     return $config; | ||||
|   } | ||||
|  | ||||
| @@ -75,27 +67,4 @@ final class PhabricatorAuditTransactionComment | ||||
|     return $this->assertAttached($this->replyToComment); | ||||
|   } | ||||
|  | ||||
|   public function getAttribute($key, $default = null) { | ||||
|     return idx($this->attributes, $key, $default); | ||||
|   } | ||||
|  | ||||
|   public function setAttribute($key, $value) { | ||||
|     $this->attributes[$key] = $value; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function newInlineCommentObject() { | ||||
|     return PhabricatorAuditInlineComment::newFromModernComment($this); | ||||
|   } | ||||
|  | ||||
|   public function getInlineContext() { | ||||
|     return $this->assertAttached($this->inlineContext); | ||||
|   } | ||||
|  | ||||
|   public function attachInlineContext( | ||||
|     PhabricatorInlineCommentContext $context = null) { | ||||
|     $this->inlineContext = $context; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										179
									
								
								src/applications/audit/view/PhabricatorAuditListView.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,179 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorAuditListView extends AphrontView { | ||||
|  | ||||
|   private $commits = array(); | ||||
|   private $header; | ||||
|   private $showDrafts; | ||||
|   private $noDataString; | ||||
|   private $highlightedAudits; | ||||
|  | ||||
|   public function setNoDataString($no_data_string) { | ||||
|     $this->noDataString = $no_data_string; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getNoDataString() { | ||||
|     return $this->noDataString; | ||||
|   } | ||||
|  | ||||
|   public function setHeader($header) { | ||||
|     $this->header = $header; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getHeader() { | ||||
|     return $this->header; | ||||
|   } | ||||
|  | ||||
|   public function setShowDrafts($show_drafts) { | ||||
|     $this->showDrafts = $show_drafts; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getShowDrafts() { | ||||
|     return $this->showDrafts; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * These commits should have both commit data and audit requests attached. | ||||
|    */ | ||||
|   public function setCommits(array $commits) { | ||||
|     assert_instances_of($commits, 'PhabricatorRepositoryCommit'); | ||||
|     $this->commits = mpull($commits, null, 'getPHID'); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function getCommits() { | ||||
|     return $this->commits; | ||||
|   } | ||||
|  | ||||
|   private function getCommitDescription($phid) { | ||||
|     if ($this->commits === null) { | ||||
|       return pht('(Unknown Commit)'); | ||||
|     } | ||||
|  | ||||
|     $commit = idx($this->commits, $phid); | ||||
|     if (!$commit) { | ||||
|       return pht('(Unknown Commit)'); | ||||
|     } | ||||
|  | ||||
|     $summary = $commit->getCommitData()->getSummary(); | ||||
|     if (strlen($summary)) { | ||||
|       return $summary; | ||||
|     } | ||||
|  | ||||
|     // No summary, so either this is still importing or just has an empty | ||||
|     // commit message. | ||||
|  | ||||
|     if (!$commit->isImported()) { | ||||
|       return pht('(Importing Commit...)'); | ||||
|     } else { | ||||
|       return pht('(Untitled Commit)'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public function render() { | ||||
|     $list = $this->buildList(); | ||||
|     $list->setFlush(true); | ||||
|     return $list->render(); | ||||
|   } | ||||
|  | ||||
|   public function buildList() { | ||||
|     $viewer = $this->getViewer(); | ||||
|     $rowc = array(); | ||||
|  | ||||
|     $phids = array(); | ||||
|     foreach ($this->getCommits() as $commit) { | ||||
|       $phids[] = $commit->getPHID(); | ||||
|  | ||||
|       foreach ($commit->getAudits() as $audit) { | ||||
|         $phids[] = $audit->getAuditorPHID(); | ||||
|       } | ||||
|  | ||||
|       $author_phid = $commit->getAuthorPHID(); | ||||
|       if ($author_phid) { | ||||
|         $phids[] = $author_phid; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $handles = $viewer->loadHandles($phids); | ||||
|  | ||||
|     $show_drafts = $this->getShowDrafts(); | ||||
|  | ||||
|     $draft_icon = id(new PHUIIconView()) | ||||
|       ->setIcon('fa-comment yellow') | ||||
|       ->addSigil('has-tooltip') | ||||
|       ->setMetadata( | ||||
|         array( | ||||
|           'tip' => pht('Unsubmitted Comments'), | ||||
|         )); | ||||
|  | ||||
|     $list = new PHUIObjectItemListView(); | ||||
|     foreach ($this->commits as $commit) { | ||||
|       $commit_phid = $commit->getPHID(); | ||||
|       $commit_handle = $handles[$commit_phid]; | ||||
|       $committed = null; | ||||
|  | ||||
|       $commit_name = $commit_handle->getName(); | ||||
|       $commit_link = $commit_handle->getURI(); | ||||
|       $commit_desc = $this->getCommitDescription($commit_phid); | ||||
|       $committed = phabricator_datetime($commit->getEpoch(), $viewer); | ||||
|  | ||||
|       $status = $commit->getAuditStatusObject(); | ||||
|  | ||||
|       $status_text = $status->getName(); | ||||
|       $status_color = $status->getColor(); | ||||
|       $status_icon = $status->getIcon(); | ||||
|  | ||||
|       $author_phid = $commit->getAuthorPHID(); | ||||
|       if ($author_phid) { | ||||
|         $author_name = $handles[$author_phid]->renderLink(); | ||||
|       } else { | ||||
|         $author_name = $commit->getCommitData()->getAuthorName(); | ||||
|       } | ||||
|  | ||||
|       $item = id(new PHUIObjectItemView()) | ||||
|         ->setObjectName($commit_name) | ||||
|         ->setHeader($commit_desc) | ||||
|         ->setHref($commit_link) | ||||
|         ->setDisabled($commit->isUnreachable()) | ||||
|         ->addByline(pht('Author: %s', $author_name)) | ||||
|         ->addIcon('none', $committed); | ||||
|  | ||||
|       if ($show_drafts) { | ||||
|         if ($commit->getHasDraft($viewer)) { | ||||
|           $item->addAttribute($draft_icon); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       $audits = $commit->getAudits(); | ||||
|       $auditor_phids = mpull($audits, 'getAuditorPHID'); | ||||
|       if ($auditor_phids) { | ||||
|         $auditor_list = $handles->newSublist($auditor_phids) | ||||
|           ->renderList() | ||||
|           ->setAsInline(true); | ||||
|       } else { | ||||
|         $auditor_list = phutil_tag('em', array(), pht('None')); | ||||
|       } | ||||
|       $item->addAttribute(pht('Auditors: %s', $auditor_list)); | ||||
|  | ||||
|       if ($status_color) { | ||||
|         $item->setStatusIcon($status_icon.' '.$status_color, $status_text); | ||||
|       } | ||||
|  | ||||
|       $list->addItem($item); | ||||
|     } | ||||
|  | ||||
|     if ($this->noDataString) { | ||||
|       $list->setNoDataString($this->noDataString); | ||||
|     } | ||||
|  | ||||
|     if ($this->header) { | ||||
|       $list->setHeader($this->header); | ||||
|     } | ||||
|  | ||||
|     return $list; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| /** | ||||
|  * Abstract interface to an identity provider or authentication source, like | ||||
|  * Twitter, Facebook, or Google. | ||||
|  * Twitter, Facebook or Google. | ||||
|  * | ||||
|  * Generally, adapters are handed some set of credentials particular to the | ||||
|  * provider they adapt, and they turn those credentials into standard | ||||
| @@ -17,37 +17,13 @@ | ||||
|  */ | ||||
| abstract class PhutilAuthAdapter extends Phobject { | ||||
|  | ||||
|   final public function getAccountIdentifiers() { | ||||
|     $result = $this->newAccountIdentifiers(); | ||||
|     assert_instances_of($result, 'PhabricatorExternalAccountIdentifier'); | ||||
|     return $result; | ||||
|   } | ||||
|  | ||||
|   protected function newAccountIdentifiers() { | ||||
|     $identifiers = array(); | ||||
|  | ||||
|     $raw_identifier = $this->getAccountID(); | ||||
|     if ($raw_identifier !== null) { | ||||
|       $identifiers[] = $this->newAccountIdentifier($raw_identifier); | ||||
|     } | ||||
|  | ||||
|     return $identifiers; | ||||
|   } | ||||
|  | ||||
|   final protected function newAccountIdentifier($raw_identifier) { | ||||
|     return id(new PhabricatorExternalAccountIdentifier()) | ||||
|       ->setIdentifierRaw($raw_identifier); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get a unique identifier associated with the account. | ||||
|    * Get a unique identifier associated with the identity. For most providers, | ||||
|    * this is an account ID. | ||||
|    * | ||||
|    * This identifier should be permanent, immutable, and uniquely identify | ||||
|    * the account. If possible, it should be nonsensitive. For providers that | ||||
|    * have a GUID or PHID value for accounts, these are the best values to use. | ||||
|    * | ||||
|    * You can implement @{method:newAccountIdentifiers} instead if a provider | ||||
|    * is unable to emit identifiers with all of these properties. | ||||
|    * The account ID needs to be unique within this adapter's configuration, such | ||||
|    * that `<adapterKey, accountID>` is globally unique and always identifies the | ||||
|    * same identity. | ||||
|    * | ||||
|    * If the adapter was unable to authenticate an identity, it should return | ||||
|    * `null`. | ||||
| @@ -55,9 +31,7 @@ abstract class PhutilAuthAdapter extends Phobject { | ||||
|    * @return string|null Unique account identifier, or `null` if authentication | ||||
|    *                     failed. | ||||
|    */ | ||||
|   public function getAccountID() { | ||||
|     throw new PhutilMethodNotImplementedException(); | ||||
|   } | ||||
|   abstract public function getAccountID(); | ||||
|  | ||||
|  | ||||
|   /** | ||||
|   | ||||
| @@ -51,17 +51,13 @@ final class PhutilGitHubAuthAdapter extends PhutilOAuthAuthAdapter { | ||||
|  | ||||
|   protected function loadOAuthAccountData() { | ||||
|     $uri = new PhutilURI('https://api.github.com/user'); | ||||
|     $uri->replaceQueryParam('access_token', $this->getAccessToken()); | ||||
|  | ||||
|     $future = new HTTPSFuture($uri); | ||||
|  | ||||
|     // NOTE: GitHub requires a User-Agent string. | ||||
|     $future->addHeader('User-Agent', __CLASS__); | ||||
|  | ||||
|     // See T13485. Circa early 2020, GitHub has deprecated use of the | ||||
|     // "access_token" URI parameter. | ||||
|     $token_header = sprintf('token %s', $this->getAccessToken()); | ||||
|     $future->addHeader('Authorization', $token_header); | ||||
|  | ||||
|     list($body) = $future->resolvex(); | ||||
|  | ||||
|     try { | ||||
|   | ||||
| @@ -13,23 +13,8 @@ final class PhutilGoogleAuthAdapter extends PhutilOAuthAuthAdapter { | ||||
|     return 'google.com'; | ||||
|   } | ||||
|  | ||||
|   protected function newAccountIdentifiers() { | ||||
|     $identifiers = array(); | ||||
|  | ||||
|     $account_id = $this->getOAuthAccountData('id'); | ||||
|     if ($account_id !== null) { | ||||
|       $account_id = sprintf( | ||||
|         'id(%s)', | ||||
|         $account_id); | ||||
|       $identifiers[] = $this->newAccountIdentifier($account_id); | ||||
|     } | ||||
|  | ||||
|     $email = $this->getAccountEmail(); | ||||
|     if ($email !== null) { | ||||
|       $identifiers[] = $this->newAccountIdentifier($email); | ||||
|     } | ||||
|  | ||||
|     return $identifiers; | ||||
|   public function getAccountID() { | ||||
|     return $this->getAccountEmail(); | ||||
|   } | ||||
|  | ||||
|   public function getAccountEmail() { | ||||
|   | ||||
| @@ -10,6 +10,7 @@ final class PhutilJIRAAuthAdapter extends PhutilOAuth1AuthAdapter { | ||||
|  | ||||
|   private $jiraBaseURI; | ||||
|   private $adapterDomain; | ||||
|   private $currentSession; | ||||
|   private $userInfo; | ||||
|  | ||||
|   public function setJIRABaseURI($jira_base_uri) { | ||||
| @@ -21,33 +22,12 @@ final class PhutilJIRAAuthAdapter extends PhutilOAuth1AuthAdapter { | ||||
|     return $this->jiraBaseURI; | ||||
|   } | ||||
|  | ||||
|   protected function newAccountIdentifiers() { | ||||
|   public function getAccountID() { | ||||
|     // Make sure the handshake is finished; this method is used for its | ||||
|     // side effect by Auth providers. | ||||
|     $this->getHandshakeData(); | ||||
|  | ||||
|     $info = $this->getUserInfo(); | ||||
|  | ||||
|     // See T13493. Older versions of JIRA provide a "key" with a username or | ||||
|     // email address. Newer versions of JIRA provide a GUID "accountId". | ||||
|     // Intermediate versions of JIRA provide both. | ||||
|  | ||||
|     $identifiers = array(); | ||||
|  | ||||
|     $account_key = idx($info, 'key'); | ||||
|     if ($account_key !== null) { | ||||
|       $identifiers[] = $this->newAccountIdentifier($account_key); | ||||
|     } | ||||
|  | ||||
|     $account_id = idx($info, 'accountId'); | ||||
|     if ($account_id !== null) { | ||||
|       $identifiers[] = $this->newAccountIdentifier( | ||||
|         sprintf( | ||||
|           'accountId(%s)', | ||||
|           $account_id)); | ||||
|     } | ||||
|  | ||||
|     return $identifiers; | ||||
|     return idx($this->getUserInfo(), 'key'); | ||||
|   } | ||||
|  | ||||
|   public function getAccountName() { | ||||
| @@ -105,36 +85,23 @@ final class PhutilJIRAAuthAdapter extends PhutilOAuth1AuthAdapter { | ||||
|  | ||||
|   private function getUserInfo() { | ||||
|     if ($this->userInfo === null) { | ||||
|       $this->userInfo = $this->newUserInfo(); | ||||
|     } | ||||
|  | ||||
|     return $this->userInfo; | ||||
|   } | ||||
|  | ||||
|   private function newUserInfo() { | ||||
|     // See T13493. Try a relatively modern (circa early 2020) API call first. | ||||
|     try { | ||||
|       return $this->newJIRAFuture('rest/api/3/myself', 'GET') | ||||
|         ->resolveJSON(); | ||||
|     } catch (Exception $ex) { | ||||
|       // If we failed the v3 call, assume the server version is too old | ||||
|       // to support this API and fall back to trying the older method. | ||||
|     } | ||||
|  | ||||
|     $session = $this->newJIRAFuture('rest/auth/1/session', 'GET') | ||||
|       $this->currentSession = $this->newJIRAFuture('rest/auth/1/session', 'GET') | ||||
|         ->resolveJSON(); | ||||
|  | ||||
|       // The session call gives us the username, but not the user key or other | ||||
|       // information. Make a second call to get additional information. | ||||
|  | ||||
|       $params = array( | ||||
|       'username' => $session['name'], | ||||
|         'username' => $this->currentSession['name'], | ||||
|       ); | ||||
|  | ||||
|     return $this->newJIRAFuture('rest/api/2/user', 'GET', $params) | ||||
|       $this->userInfo = $this->newJIRAFuture('rest/api/2/user', 'GET', $params) | ||||
|         ->resolveJSON(); | ||||
|     } | ||||
|  | ||||
|     return $this->userInfo; | ||||
|   } | ||||
|  | ||||
|   public static function newJIRAKeypair() { | ||||
|     $config = array( | ||||
|       'digest_alg' => 'sha512', | ||||
|   | ||||
| @@ -156,7 +156,7 @@ abstract class PhutilOAuth1AuthAdapter extends PhutilAuthAdapter { | ||||
|     $authorize_token_uri = new PhutilURI($this->getAuthorizeTokenURI()); | ||||
|     $authorize_token_uri->replaceQueryParam('oauth_token', $this->getToken()); | ||||
|  | ||||
|     return phutil_string_cast($authorize_token_uri); | ||||
|     return (string)$authorize_token_uri; | ||||
|   } | ||||
|  | ||||
|   protected function finishOAuthHandshake() { | ||||
|   | ||||
| @@ -73,7 +73,7 @@ final class PhabricatorAuthApplication extends PhabricatorApplication { | ||||
|         'session/downgrade/' | ||||
|           => 'PhabricatorAuthDowngradeSessionController', | ||||
|         'enroll/' => array( | ||||
|           '(?:(?P<pageKey>[^/]+)/)?' | ||||
|           '(?:(?P<pageKey>[^/]+)/)?(?:(?P<formSaved>saved)/)?' | ||||
|             => 'PhabricatorAuthNeedsMultiFactorController', | ||||
|         ), | ||||
|         'sshkey/' => array( | ||||
|   | ||||
| @@ -63,13 +63,6 @@ final class PhabricatorCookies extends Phobject { | ||||
|   const COOKIE_INVITE = 'invite'; | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Stores a workflow completion across a redirect-after-POST following a | ||||
|    * form submission. This can be used to show "Changes Saved" messages. | ||||
|    */ | ||||
|   const COOKIE_SUBMIT = 'phfrm'; | ||||
|  | ||||
|  | ||||
| /* -(  Client ID Cookie  )--------------------------------------------------- */ | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -197,6 +197,22 @@ abstract class PhabricatorAuthController extends PhabricatorController { | ||||
|       return array($account, $provider, $response); | ||||
|     } | ||||
|  | ||||
|     $other_account = id(new PhabricatorExternalAccount())->loadAllWhere( | ||||
|       'accountType = %s AND accountDomain = %s AND accountID = %s | ||||
|         AND id != %d', | ||||
|       $account->getAccountType(), | ||||
|       $account->getAccountDomain(), | ||||
|       $account->getAccountID(), | ||||
|       $account->getID()); | ||||
|  | ||||
|     if ($other_account) { | ||||
|       $response = $this->renderError( | ||||
|         pht( | ||||
|           'The account you are attempting to register with already belongs '. | ||||
|           'to another user.')); | ||||
|       return array($account, $provider, $response); | ||||
|     } | ||||
|  | ||||
|     $config = $account->getProviderConfig(); | ||||
|     if (!$config->getIsEnabled()) { | ||||
|       $response = $this->renderError( | ||||
|   | ||||
| @@ -116,21 +116,14 @@ final class PhabricatorAuthLoginController | ||||
|         } | ||||
|       } else { | ||||
|  | ||||
|         // If the user already has a linked account on this provider, prevent | ||||
|         // them from linking a second account. This can happen if they swap | ||||
|         // logins and then refresh the account link. | ||||
|  | ||||
|         // There's no technical reason we can't allow you to link multiple | ||||
|         // accounts from a single provider; disallowing this is currently a | ||||
|         // product deciison. See T2549. | ||||
|  | ||||
|         // If the user already has a linked account of this type, prevent them | ||||
|         // from linking a second account. This can happen if they swap logins | ||||
|         // and then refresh the account link. See T6707. We will eventually | ||||
|         // allow this after T2549. | ||||
|         $existing_accounts = id(new PhabricatorExternalAccountQuery()) | ||||
|           ->setViewer($viewer) | ||||
|           ->withUserPHIDs(array($viewer->getPHID())) | ||||
|           ->withProviderConfigPHIDs( | ||||
|             array( | ||||
|               $provider->getProviderConfigPHID(), | ||||
|             )) | ||||
|           ->withAccountTypes(array($account->getAccountType())) | ||||
|           ->execute(); | ||||
|         if ($existing_accounts) { | ||||
|           return $this->renderError( | ||||
|   | ||||
| @@ -346,6 +346,14 @@ final class PhabricatorAuthRegisterController | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // blender hack | ||||
|       $root = dirname(phutil_get_library_root('phabricator')); | ||||
|       require $root.'/migration/dedup.php'; | ||||
|       if (array_key_exists($request->getStr('username'), $migrate_dedup_users)) { | ||||
|         $e_username = pht('Duplicate'); | ||||
|         $errors[] = pht('Username is already reserved.'); | ||||
|       } | ||||
|  | ||||
|       if (!$errors) { | ||||
|         if (!$is_setup) { | ||||
|           $image = $this->loadProfilePicture($account); | ||||
| @@ -457,6 +465,7 @@ final class PhabricatorAuthRegisterController | ||||
|  | ||||
|             if (!$is_setup) { | ||||
|               $account->setUserPHID($user->getPHID()); | ||||
|               $provider->willRegisterAccount($account); | ||||
|               $account->save(); | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -67,7 +67,7 @@ final class PhabricatorAuthUnlinkController | ||||
|       ->setWorkflowKey($workflow_key) | ||||
|       ->requireHighSecurityToken($viewer, $request, $done_uri); | ||||
|  | ||||
|     $account->unlinkAccount(); | ||||
|     $account->delete(); | ||||
|  | ||||
|     id(new PhabricatorAuthSessionEngine())->terminateLoginSessions( | ||||
|       $viewer, | ||||
|   | ||||
| @@ -181,12 +181,6 @@ final class PhabricatorAuthPasswordEngine | ||||
|     $normal_password = phutil_utf8_strtolower($raw_password); | ||||
|     if (strlen($normal_password) >= $minimum_similarity) { | ||||
|       foreach ($normal_map as $term => $source) { | ||||
|  | ||||
|         // See T2312. This may be required if the term list includes numeric | ||||
|         // strings like "12345", which will be cast to integers when used as | ||||
|         // array keys. | ||||
|         $term = phutil_string_cast($term); | ||||
|  | ||||
|         if (strpos($term, $normal_password) === false && | ||||
|             strpos($normal_password, $term) === false) { | ||||
|           continue; | ||||
|   | ||||
| @@ -56,12 +56,9 @@ final class PhabricatorAuthManagementLDAPWorkflow | ||||
|     $console->writeOut("\n"); | ||||
|     $console->writeOut("%s\n", pht('Connecting to LDAP...')); | ||||
|  | ||||
|     $account_ids = $adapter->getAccountIdentifiers(); | ||||
|     if ($account_ids) { | ||||
|       $value_list = mpull($account_ids, 'getIdentifierRaw'); | ||||
|       $value_list = implode(', ', $value_list); | ||||
|  | ||||
|       $console->writeOut("%s\n", pht('Found LDAP Account: %s', $value_list)); | ||||
|     $account_id = $adapter->getAccountID(); | ||||
|     if ($account_id) { | ||||
|       $console->writeOut("%s\n", pht('Found LDAP Account: %s', $account_id)); | ||||
|     } else { | ||||
|       $console->writeOut("%s\n", pht('Unable to find LDAP account!')); | ||||
|     } | ||||
|   | ||||
| @@ -18,6 +18,16 @@ final class PhabricatorAuthManagementRefreshWorkflow | ||||
|             'param' => 'user', | ||||
|             'help' => pht('Refresh tokens for a given user.'), | ||||
|           ), | ||||
|           array( | ||||
|             'name' => 'type', | ||||
|             'param' => 'provider', | ||||
|             'help' => pht('Refresh tokens for a given provider type.'), | ||||
|           ), | ||||
|           array( | ||||
|             'name' => 'domain', | ||||
|             'param' => 'domain', | ||||
|             'help' => pht('Refresh tokens for a given domain.'), | ||||
|           ), | ||||
|         )); | ||||
|   } | ||||
|  | ||||
| @@ -47,6 +57,17 @@ final class PhabricatorAuthManagementRefreshWorkflow | ||||
|       } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     $type = $args->getArg('type'); | ||||
|     if (strlen($type)) { | ||||
|       $query->withAccountTypes(array($type)); | ||||
|     } | ||||
|  | ||||
|     $domain = $args->getArg('domain'); | ||||
|     if (strlen($domain)) { | ||||
|       $query->withAccountDomains(array($domain)); | ||||
|     } | ||||
|  | ||||
|     $accounts = $query->execute(); | ||||
|  | ||||
|     if (!$accounts) { | ||||
| @@ -61,24 +82,25 @@ final class PhabricatorAuthManagementRefreshWorkflow | ||||
|     } | ||||
|  | ||||
|     $providers = PhabricatorAuthProvider::getAllEnabledProviders(); | ||||
|     $providers = mpull($providers, null, 'getProviderConfigPHID'); | ||||
|  | ||||
|     foreach ($accounts as $account) { | ||||
|       $console->writeOut( | ||||
|         "%s\n", | ||||
|         pht( | ||||
|           'Refreshing account #%d.', | ||||
|           $account->getID())); | ||||
|           'Refreshing account #%d (%s/%s).', | ||||
|           $account->getID(), | ||||
|           $account->getAccountType(), | ||||
|           $account->getAccountDomain())); | ||||
|  | ||||
|       $config_phid = $account->getProviderConfigPHID(); | ||||
|       if (empty($providers[$config_phid])) { | ||||
|       $key = $account->getProviderKey(); | ||||
|       if (empty($providers[$key])) { | ||||
|         $console->writeOut( | ||||
|           "> %s\n", | ||||
|           pht('Skipping, provider is not enabled or does not exist.')); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       $provider = $providers[$config_phid]; | ||||
|       $provider = $providers[$key]; | ||||
|       if (!($provider instanceof PhabricatorOAuth2AuthProvider)) { | ||||
|         $console->writeOut( | ||||
|           "> %s\n", | ||||
|   | ||||
| @@ -20,10 +20,6 @@ abstract class PhabricatorAuthProvider extends Phobject { | ||||
|     return $this->providerConfig; | ||||
|   } | ||||
|  | ||||
|   public function getProviderConfigPHID() { | ||||
|     return $this->getProviderConfig()->getPHID(); | ||||
|   } | ||||
|  | ||||
|   public function getConfigurationHelp() { | ||||
|     return null; | ||||
|   } | ||||
| @@ -190,86 +186,44 @@ abstract class PhabricatorAuthProvider extends Phobject { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   final protected function newExternalAccountForIdentifiers( | ||||
|     array $identifiers) { | ||||
|   public function willRegisterAccount(PhabricatorExternalAccount $account) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|     assert_instances_of($identifiers, 'PhabricatorExternalAccountIdentifier'); | ||||
|   protected function loadOrCreateAccount($account_id) { | ||||
|     if (!strlen($account_id)) { | ||||
|       throw new Exception(pht('Empty account ID!')); | ||||
|     } | ||||
|  | ||||
|     if (!$identifiers) { | ||||
|     $adapter = $this->getAdapter(); | ||||
|     $adapter_class = get_class($adapter); | ||||
|  | ||||
|     if (!strlen($adapter->getAdapterType())) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Authentication provider (of class "%s") is attempting to '. | ||||
|           'load or create an external account, but provided no account '. | ||||
|           'identifiers.', | ||||
|           get_class($this))); | ||||
|           "AuthAdapter (of class '%s') has an invalid implementation: ". | ||||
|           "no adapter type.", | ||||
|           $adapter_class)); | ||||
|     } | ||||
|  | ||||
|     $config = $this->getProviderConfig(); | ||||
|     $viewer = PhabricatorUser::getOmnipotentUser(); | ||||
|  | ||||
|     $raw_identifiers = mpull($identifiers, 'getIdentifierRaw'); | ||||
|  | ||||
|     $accounts = id(new PhabricatorExternalAccountQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->withProviderConfigPHIDs(array($config->getPHID())) | ||||
|       ->withRawAccountIdentifiers($raw_identifiers) | ||||
|       ->needAccountIdentifiers(true) | ||||
|       ->execute(); | ||||
|     if (!$accounts) { | ||||
|       $account = $this->newExternalAccount(); | ||||
|     } else if (count($accounts) === 1) { | ||||
|       $account = head($accounts); | ||||
|     } else { | ||||
|     if (!strlen($adapter->getAdapterDomain())) { | ||||
|       throw new Exception( | ||||
|         pht( | ||||
|           'Authentication provider (of class "%s") is attempting to load '. | ||||
|           'or create an external account, but provided a list of '. | ||||
|           'account identifiers which map to more than one account: %s.', | ||||
|           get_class($this), | ||||
|           implode(', ', $raw_identifiers))); | ||||
|           "AuthAdapter (of class '%s') has an invalid implementation: ". | ||||
|           "no adapter domain.", | ||||
|           $adapter_class)); | ||||
|     } | ||||
|  | ||||
|     // See T13493. Add all the identifiers to the account. In the case where | ||||
|     // an account initially has a lower-quality identifier (like an email | ||||
|     // address) and later adds a higher-quality identifier (like a GUID), this | ||||
|     // allows us to automatically upgrade toward the higher-quality identifier | ||||
|     // and survive API changes which remove the lower-quality identifier more | ||||
|     // gracefully. | ||||
|  | ||||
|     foreach ($identifiers as $identifier) { | ||||
|       $account->appendIdentifier($identifier); | ||||
|     } | ||||
|  | ||||
|     return $this->didUpdateAccount($account); | ||||
|   } | ||||
|  | ||||
|   final protected function newExternalAccountForUser(PhabricatorUser $user) { | ||||
|     $config = $this->getProviderConfig(); | ||||
|  | ||||
|     // When a user logs in with a provider like username/password, they | ||||
|     // always already have a Phabricator account (since there's no way they | ||||
|     // could have a username otherwise). | ||||
|  | ||||
|     // These users should never go to registration, so we're building a | ||||
|     // dummy "external account" which just links directly back to their | ||||
|     // internal account. | ||||
|  | ||||
|     $account = id(new PhabricatorExternalAccountQuery()) | ||||
|       ->setViewer($user) | ||||
|       ->withProviderConfigPHIDs(array($config->getPHID())) | ||||
|       ->withUserPHIDs(array($user->getPHID())) | ||||
|       ->executeOne(); | ||||
|     $account = id(new PhabricatorExternalAccount())->loadOneWhere( | ||||
|       'accountType = %s AND accountDomain = %s AND accountID = %s', | ||||
|       $adapter->getAdapterType(), | ||||
|       $adapter->getAdapterDomain(), | ||||
|       $account_id); | ||||
|     if (!$account) { | ||||
|       $account = $this->newExternalAccount() | ||||
|         ->setUserPHID($user->getPHID()); | ||||
|         ->setAccountID($account_id); | ||||
|     } | ||||
|  | ||||
|     return $this->didUpdateAccount($account); | ||||
|   } | ||||
|  | ||||
|   private function didUpdateAccount(PhabricatorExternalAccount $account) { | ||||
|     $adapter = $this->getAdapter(); | ||||
|  | ||||
|     $account->setUsername($adapter->getAccountName()); | ||||
|     $account->setRealName($adapter->getAccountRealName()); | ||||
|     $account->setEmail($adapter->getAccountEmail()); | ||||
| @@ -286,7 +240,6 @@ abstract class PhabricatorAuthProvider extends Phobject { | ||||
|         // file entry for it, but there's no convenient way to do this with | ||||
|         // PhabricatorFile right now. The storage will get shared, so the impact | ||||
|         // here is negligible. | ||||
|  | ||||
|         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | ||||
|           $image_file = PhabricatorFile::newFromFileDownload( | ||||
|             $image_uri, | ||||
| @@ -352,23 +305,10 @@ abstract class PhabricatorAuthProvider extends Phobject { | ||||
|     $config = $this->getProviderConfig(); | ||||
|     $adapter = $this->getAdapter(); | ||||
|  | ||||
|     $account = id(new PhabricatorExternalAccount()) | ||||
|       ->setProviderConfigPHID($config->getPHID()) | ||||
|       ->attachAccountIdentifiers(array()); | ||||
|  | ||||
|     // TODO: Remove this when these columns are removed. They no longer have | ||||
|     // readers or writers (other than this callsite). | ||||
|  | ||||
|     $account | ||||
|     return id(new PhabricatorExternalAccount()) | ||||
|       ->setAccountType($adapter->getAdapterType()) | ||||
|       ->setAccountDomain($adapter->getAdapterDomain()); | ||||
|  | ||||
|     // TODO: Remove this when "accountID" is removed; the column is not | ||||
|     // nullable. | ||||
|  | ||||
|     $account->setAccountID(''); | ||||
|  | ||||
|     return $account; | ||||
|       ->setAccountDomain($adapter->getAdapterDomain()) | ||||
|       ->setProviderConfigPHID($config->getPHID()); | ||||
|   } | ||||
|  | ||||
|   public function getLoginOrder() { | ||||
|   | ||||
| @@ -335,7 +335,7 @@ final class PhabricatorJIRAAuthProvider | ||||
|   public function getDoorkeeperURIRef(PhutilURI $uri) { | ||||
|     $uri_string = phutil_string_cast($uri); | ||||
|  | ||||
|     $pattern = '((https?://\S+?)/browse/([A-Z][A-Z0-9]*-[1-9]\d*))'; | ||||
|     $pattern = '((https?://\S+?)/browse/([A-Z]+-[1-9]\d*))'; | ||||
|     $matches = null; | ||||
|     if (!preg_match($pattern, $uri_string, $matches)) { | ||||
|       return null; | ||||
|   | ||||
| @@ -164,7 +164,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider { | ||||
|           // See T3351. | ||||
|  | ||||
|           DarkConsoleErrorLogPluginAPI::enableDiscardMode(); | ||||
|             $identifiers = $adapter->getAccountIdentifiers(); | ||||
|             $account_id = $adapter->getAccountID(); | ||||
|           DarkConsoleErrorLogPluginAPI::disableDiscardMode(); | ||||
|         } else { | ||||
|           throw new Exception(pht('Username and password are required!')); | ||||
| @@ -180,9 +180,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $account = $this->newExternalAccountForIdentifiers($identifiers); | ||||
|  | ||||
|     return array($account, $response); | ||||
|     return array($this->loadOrCreateAccount($account_id), $response); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -100,13 +100,13 @@ abstract class PhabricatorOAuth1AuthProvider | ||||
|     // an access token. | ||||
|  | ||||
|     try { | ||||
|       $identifiers = $adapter->getAccountIdentifiers(); | ||||
|       $account_id = $adapter->getAccountID(); | ||||
|     } catch (Exception $ex) { | ||||
|       // TODO: Handle this in a more user-friendly way. | ||||
|       throw $ex; | ||||
|     } | ||||
|  | ||||
|     if (!$identifiers) { | ||||
|     if (!strlen($account_id)) { | ||||
|       $response = $controller->buildProviderErrorResponse( | ||||
|         $this, | ||||
|         pht( | ||||
| @@ -115,9 +115,7 @@ abstract class PhabricatorOAuth1AuthProvider | ||||
|       return array($account, $response); | ||||
|     } | ||||
|  | ||||
|     $account = $this->newExternalAccountForIdentifiers($identifiers); | ||||
|  | ||||
|     return array($account, $response); | ||||
|     return array($this->loadOrCreateAccount($account_id), $response); | ||||
|   } | ||||
|  | ||||
|   public function processEditForm( | ||||
|   | ||||
| @@ -80,13 +80,13 @@ abstract class PhabricatorOAuth2AuthProvider | ||||
|     // an access token. | ||||
|  | ||||
|     try { | ||||
|       $identifiers = $adapter->getAccountIdentifiers(); | ||||
|       $account_id = $adapter->getAccountID(); | ||||
|     } catch (Exception $ex) { | ||||
|       // TODO: Handle this in a more user-friendly way. | ||||
|       throw $ex; | ||||
|     } | ||||
|  | ||||
|     if (!$identifiers) { | ||||
|     if (!strlen($account_id)) { | ||||
|       $response = $controller->buildProviderErrorResponse( | ||||
|         $this, | ||||
|         pht( | ||||
| @@ -95,9 +95,7 @@ abstract class PhabricatorOAuth2AuthProvider | ||||
|       return array($account, $response); | ||||
|     } | ||||
|  | ||||
|     $account = $this->newExternalAccountForIdentifiers($identifiers); | ||||
|  | ||||
|     return array($account, $response); | ||||
|     return array($this->loadOrCreateAccount($account_id), $response); | ||||
|   } | ||||
|  | ||||
|   public function processEditForm( | ||||
| @@ -199,7 +197,7 @@ abstract class PhabricatorOAuth2AuthProvider | ||||
|     PhabricatorExternalAccount $account, | ||||
|     $force_refresh = false) { | ||||
|  | ||||
|     if ($account->getProviderConfigPHID() !== $this->getProviderConfigPHID()) { | ||||
|     if ($account->getProviderKey() !== $this->getProviderKey()) { | ||||
|       throw new Exception(pht('Account does not match provider!')); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -173,8 +173,8 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|     $dialog = id(new AphrontDialogView()) | ||||
|       ->setSubmitURI($this->getLoginURI()) | ||||
|       ->setUser($viewer) | ||||
|       ->setTitle(pht('Log In')) | ||||
|       ->addSubmitButton(pht('Log In')); | ||||
|       ->setTitle(pht('Login to developer.blender.org')) | ||||
|       ->addSubmitButton(pht('Login')); | ||||
|  | ||||
|     if ($this->shouldAllowRegistration()) { | ||||
|       $dialog->addCancelButton( | ||||
| @@ -182,6 +182,11 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|         pht('Register New Account')); | ||||
|     } | ||||
|  | ||||
|     $webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/'; | ||||
|     $dialog->addFooter( | ||||
|       phutil_safe_html( | ||||
|         FileSystem::readFile($webroot .'rsrc/custom/static/login.html'))); | ||||
|  | ||||
|     $dialog->addFooter( | ||||
|       phutil_tag( | ||||
|         'a', | ||||
| @@ -217,6 +222,28 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|       $errors[] = pht('Username or password are incorrect.'); | ||||
|     } | ||||
|  | ||||
|     if (true) { | ||||
|       // blender hack | ||||
|       $root = dirname(phutil_get_library_root('phabricator')); | ||||
|       require $root.'/migration/dedup.php'; | ||||
|  | ||||
|       $missing_username = $request->getStr('username'); | ||||
|  | ||||
|       $find_user = id(new PhabricatorUser())->loadOneWhere( | ||||
|         'username = %s', | ||||
|          $missing_username); | ||||
|  | ||||
|       if (!$find_user && array_key_exists($missing_username, $migrate_dedup_users)) { | ||||
|         $errors = array(); | ||||
|         $errors[] = pht('This account was merged into account "' . | ||||
|                         $migrate_dedup_users[$missing_username] . | ||||
|                         '", because Phabricator does not support multiple accounts with the same email address. ' . | ||||
|                         'Please login with that account instead ' . | ||||
|                         '(optionally recovering your password if you forgot it). ' . | ||||
|                         'After logging in you will be able to change your username in the User Settings.'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if ($errors) { | ||||
|       $errors = id(new PHUIInfoView())->setErrors($errors); | ||||
|     } | ||||
| @@ -305,7 +332,7 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|               ->setObject($user); | ||||
|  | ||||
|             if ($engine->isValidPassword($envelope)) { | ||||
|               $account = $this->newExternalAccountForUser($user); | ||||
|               $account = $this->loadOrCreateAccount($user->getPHID()); | ||||
|               $log_user = $user; | ||||
|             } | ||||
|           } | ||||
| @@ -339,6 +366,16 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   protected function willSaveAccount(PhabricatorExternalAccount $account) { | ||||
|     parent::willSaveAccount($account); | ||||
|     $account->setUserPHID($account->getAccountID()); | ||||
|   } | ||||
|  | ||||
|   public function willRegisterAccount(PhabricatorExternalAccount $account) { | ||||
|     parent::willRegisterAccount($account); | ||||
|     $account->setAccountID($account->getUserPHID()); | ||||
|   } | ||||
|  | ||||
|   public static function getPasswordProvider() { | ||||
|     $providers = self::getAllEnabledProviders(); | ||||
|  | ||||
| @@ -365,5 +402,4 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|   public function shouldAllowEmailTrustConfiguration() { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,94 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorExternalAccountIdentifierQuery | ||||
|   extends PhabricatorCursorPagedPolicyAwareQuery { | ||||
|  | ||||
|   private $ids; | ||||
|   private $phids; | ||||
|   private $providerConfigPHIDs; | ||||
|   private $externalAccountPHIDs; | ||||
|   private $rawIdentifiers; | ||||
|  | ||||
|   public function withIDs($ids) { | ||||
|     $this->ids = $ids; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withPHIDs(array $phids) { | ||||
|     $this->phids = $phids; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withProviderConfigPHIDs(array $phids) { | ||||
|     $this->providerConfigPHIDs = $phids; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withExternalAccountPHIDs(array $phids) { | ||||
|     $this->externalAccountPHIDs = $phids; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withRawIdentifiers(array $identifiers) { | ||||
|     $this->rawIdentifiers = $identifiers; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function newResultObject() { | ||||
|     return new PhabricatorExternalAccountIdentifier(); | ||||
|   } | ||||
|  | ||||
|   protected function loadPage() { | ||||
|     return $this->loadStandardPage($this->newResultObject()); | ||||
|   } | ||||
|  | ||||
|   protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { | ||||
|     $where = parent::buildWhereClauseParts($conn); | ||||
|  | ||||
|     if ($this->ids !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'id IN (%Ld)', | ||||
|         $this->ids); | ||||
|     } | ||||
|  | ||||
|     if ($this->phids !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'phid IN (%Ls)', | ||||
|         $this->phids); | ||||
|     } | ||||
|  | ||||
|     if ($this->providerConfigPHIDs !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'providerConfigPHID IN (%Ls)', | ||||
|         $this->providerConfigPHIDs); | ||||
|     } | ||||
|  | ||||
|     if ($this->externalAccountPHIDs !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'externalAccountPHID IN (%Ls)', | ||||
|         $this->externalAccountPHIDs); | ||||
|     } | ||||
|  | ||||
|     if ($this->rawIdentifiers !== null) { | ||||
|       $hashes = array(); | ||||
|       foreach ($this->rawIdentifiers as $raw_identifier) { | ||||
|         $hashes[] = PhabricatorHash::digestForIndex($raw_identifier); | ||||
|       } | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'identifierHash IN (%Ls)', | ||||
|         $hashes); | ||||
|     } | ||||
|  | ||||
|     return $where; | ||||
|   } | ||||
|  | ||||
|   public function getQueryApplicationClass() { | ||||
|     return 'PhabricatorPeopleApplication'; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -15,18 +15,34 @@ final class PhabricatorExternalAccountQuery | ||||
|  | ||||
|   private $ids; | ||||
|   private $phids; | ||||
|   private $accountTypes; | ||||
|   private $accountDomains; | ||||
|   private $accountIDs; | ||||
|   private $userPHIDs; | ||||
|   private $needImages; | ||||
|   private $accountSecrets; | ||||
|   private $providerConfigPHIDs; | ||||
|   private $needAccountIdentifiers; | ||||
|   private $rawAccountIdentifiers; | ||||
|  | ||||
|   public function withUserPHIDs(array $user_phids) { | ||||
|     $this->userPHIDs = $user_phids; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withAccountIDs(array $account_ids) { | ||||
|     $this->accountIDs = $account_ids; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withAccountDomains(array $account_domains) { | ||||
|     $this->accountDomains = $account_domains; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withAccountTypes(array $account_types) { | ||||
|     $this->accountTypes = $account_types; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withPHIDs(array $phids) { | ||||
|     $this->phids = $phids; | ||||
|     return $this; | ||||
| @@ -47,21 +63,11 @@ final class PhabricatorExternalAccountQuery | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function needAccountIdentifiers($need) { | ||||
|     $this->needAccountIdentifiers = $need; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withProviderConfigPHIDs(array $phids) { | ||||
|     $this->providerConfigPHIDs = $phids; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function withRawAccountIdentifiers(array $identifiers) { | ||||
|     $this->rawAccountIdentifiers = $identifiers; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function newResultObject() { | ||||
|     return new PhabricatorExternalAccount(); | ||||
|   } | ||||
| @@ -126,23 +132,6 @@ final class PhabricatorExternalAccountQuery | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if ($this->needAccountIdentifiers) { | ||||
|       $account_phids = mpull($accounts, 'getPHID'); | ||||
|  | ||||
|       $identifiers = id(new PhabricatorExternalAccountIdentifierQuery()) | ||||
|         ->setViewer($viewer) | ||||
|         ->setParentQuery($this) | ||||
|         ->withExternalAccountPHIDs($account_phids) | ||||
|         ->execute(); | ||||
|  | ||||
|       $identifiers = mgroup($identifiers, 'getExternalAccountPHID'); | ||||
|       foreach ($accounts as $account) { | ||||
|         $account_phid = $account->getPHID(); | ||||
|         $account_identifiers = idx($identifiers, $account_phid, array()); | ||||
|         $account->attachAccountIdentifiers($account_identifiers); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $accounts; | ||||
|   } | ||||
|  | ||||
| @@ -152,98 +141,62 @@ final class PhabricatorExternalAccountQuery | ||||
|     if ($this->ids !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'account.id IN (%Ld)', | ||||
|         'id IN (%Ld)', | ||||
|         $this->ids); | ||||
|     } | ||||
|  | ||||
|     if ($this->phids !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'account.phid IN (%Ls)', | ||||
|         'phid IN (%Ls)', | ||||
|         $this->phids); | ||||
|     } | ||||
|  | ||||
|     if ($this->accountTypes !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'accountType IN (%Ls)', | ||||
|         $this->accountTypes); | ||||
|     } | ||||
|  | ||||
|     if ($this->accountDomains !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'accountDomain IN (%Ls)', | ||||
|         $this->accountDomains); | ||||
|     } | ||||
|  | ||||
|     if ($this->accountIDs !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'accountID IN (%Ls)', | ||||
|         $this->accountIDs); | ||||
|     } | ||||
|  | ||||
|     if ($this->userPHIDs !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'account.userPHID IN (%Ls)', | ||||
|         'userPHID IN (%Ls)', | ||||
|         $this->userPHIDs); | ||||
|     } | ||||
|  | ||||
|     if ($this->accountSecrets !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'account.accountSecret IN (%Ls)', | ||||
|         'accountSecret IN (%Ls)', | ||||
|         $this->accountSecrets); | ||||
|     } | ||||
|  | ||||
|     if ($this->providerConfigPHIDs !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'account.providerConfigPHID IN (%Ls)', | ||||
|         'providerConfigPHID IN (%Ls)', | ||||
|         $this->providerConfigPHIDs); | ||||
|  | ||||
|       // If we have a list of ProviderConfig PHIDs and are joining the | ||||
|       // identifiers table, also include the list as an additional constraint | ||||
|       // on the identifiers table. | ||||
|  | ||||
|       // This does not change the query results (an Account and its | ||||
|       // Identifiers always have the same ProviderConfig PHID) but it allows | ||||
|       // us to use keys on the Identifier table more efficiently. | ||||
|  | ||||
|       if ($this->shouldJoinIdentifiersTable()) { | ||||
|         $where[] = qsprintf( | ||||
|           $conn, | ||||
|           'identifier.providerConfigPHID IN (%Ls)', | ||||
|           $this->providerConfigPHIDs); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if ($this->rawAccountIdentifiers !== null) { | ||||
|       $hashes = array(); | ||||
|  | ||||
|       foreach ($this->rawAccountIdentifiers as $raw_identifier) { | ||||
|         $hashes[] = PhabricatorHash::digestForIndex($raw_identifier); | ||||
|       } | ||||
|  | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'identifier.identifierHash IN (%Ls)', | ||||
|         $hashes); | ||||
|     } | ||||
|  | ||||
|     return $where; | ||||
|   } | ||||
|  | ||||
|   protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { | ||||
|     $joins = parent::buildJoinClauseParts($conn); | ||||
|  | ||||
|     if ($this->shouldJoinIdentifiersTable()) { | ||||
|       $joins[] = qsprintf( | ||||
|         $conn, | ||||
|         'JOIN %R identifier ON account.phid = identifier.externalAccountPHID', | ||||
|         new PhabricatorExternalAccountIdentifier()); | ||||
|     } | ||||
|  | ||||
|     return $joins; | ||||
|   } | ||||
|  | ||||
|   protected function shouldJoinIdentifiersTable() { | ||||
|     return ($this->rawAccountIdentifiers !== null); | ||||
|   } | ||||
|  | ||||
|   protected function shouldGroupQueryResultRows() { | ||||
|     if ($this->shouldJoinIdentifiersTable()) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     return parent::shouldGroupQueryResultRows(); | ||||
|   } | ||||
|  | ||||
|   protected function getPrimaryTableAlias() { | ||||
|     return 'account'; | ||||
|   } | ||||
|  | ||||
|   public function getQueryApplicationClass() { | ||||
|     return 'PhabricatorPeopleApplication'; | ||||
|   } | ||||
|   | ||||
| @@ -80,7 +80,7 @@ final class PhabricatorAuthSSHPrivateKey extends Phobject { | ||||
|     if (!$err) { | ||||
|       if ($passphrase) { | ||||
|         execx( | ||||
|           'ssh-keygen -p -P %P -N %s -f %R', | ||||
|           'ssh-keygen -y -P %P -N %s -f %R', | ||||
|           $passphrase, | ||||
|           '', | ||||
|           $tmp); | ||||
|   | ||||
| @@ -125,7 +125,13 @@ final class PhabricatorAuthPassword | ||||
|     $hash = $hasher->getPasswordHashForStorage($digest); | ||||
|     $raw_hash = $hash->openEnvelope(); | ||||
|  | ||||
|     return $this->setPasswordHash($raw_hash); | ||||
|     $result = $this->setPasswordHash($raw_hash); | ||||
|     if ($result) { | ||||
|       if ($object instanceof PhabricatorUser) { | ||||
|         $object->updateHtaccessPassword($password); | ||||
|       } | ||||
|     } | ||||
|     return $result; | ||||
|   } | ||||
|  | ||||
|   public function comparePassword( | ||||
|   | ||||
| @@ -4,8 +4,7 @@ final class PhabricatorAuthProviderConfig | ||||
|   extends PhabricatorAuthDAO | ||||
|   implements | ||||
|     PhabricatorApplicationTransactionInterface, | ||||
|     PhabricatorPolicyInterface, | ||||
|     PhabricatorDestructibleInterface { | ||||
|     PhabricatorPolicyInterface { | ||||
|  | ||||
|   protected $providerClass; | ||||
|   protected $providerType; | ||||
| @@ -141,33 +140,4 @@ final class PhabricatorAuthProviderConfig | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|  | ||||
| /* -(  PhabricatorDestructibleInterface  )----------------------------------- */ | ||||
|  | ||||
|  | ||||
|   public function destroyObjectPermanently( | ||||
|     PhabricatorDestructionEngine $engine) { | ||||
|  | ||||
|     $viewer = $engine->getViewer(); | ||||
|     $config_phid = $this->getPHID(); | ||||
|  | ||||
|     $accounts = id(new PhabricatorExternalAccountQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->withProviderConfigPHIDs(array($config_phid)) | ||||
|       ->newIterator(); | ||||
|     foreach ($accounts as $account) { | ||||
|       $engine->destroyObject($account); | ||||
|     } | ||||
|  | ||||
|     $identifiers = id(new PhabricatorExternalAccountIdentifierQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->withProviderConfigPHIDs(array($config_phid)) | ||||
|       ->newIterator(); | ||||
|     foreach ($identifiers as $identifier) { | ||||
|       $engine->destroyObject($identifier); | ||||
|     } | ||||
|  | ||||
|     $this->delete(); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -37,6 +37,8 @@ final class PhabricatorAuthAccountView extends AphrontView { | ||||
|       $use_name = $username; | ||||
|     } else if (strlen($realname)) { | ||||
|       $use_name = $realname; | ||||
|     } else { | ||||
|       $use_name = $account->getAccountID(); | ||||
|     } | ||||
|  | ||||
|     $content[] = phutil_tag( | ||||
| @@ -59,6 +61,8 @@ final class PhabricatorAuthAccountView extends AphrontView { | ||||
|       ), | ||||
|       array( | ||||
|         $prov_name, | ||||
|         " \xC2\xB7 ", | ||||
|         $account->getAccountID(), | ||||
|       )); | ||||
|  | ||||
|     $account_uri = $account->getAccountURI(); | ||||
|   | ||||
| @@ -849,8 +849,8 @@ final class PhutilICSParser extends Phobject { | ||||
|       ); | ||||
|  | ||||
|       // Load the map of Windows timezones. | ||||
|       $root_path = dirname(phutil_get_library_root('phabricator')); | ||||
|       $windows_path = $root_path.'/resources/timezones/windows-timezones.json'; | ||||
|       $root_path = dirname(phutil_get_library_root('phutil')); | ||||
|       $windows_path = $root_path.'/resources/timezones/windows_timezones.json'; | ||||
|       $windows_data = Filesystem::readFile($windows_path); | ||||
|       $windows_zones = phutil_json_decode($windows_data); | ||||
|  | ||||
|   | ||||
| @@ -82,7 +82,6 @@ final class CelerityDefaultPostprocessor | ||||
|       'alphablack'          => '0,0,0', | ||||
|  | ||||
|       // Base Greys | ||||
|       'thingreyborder'     => '#dadee8', | ||||
|       'lightgreyborder'     => '#C7CCD9', | ||||
|       'greyborder'          => '#A1A6B0', | ||||
|       'darkgreyborder'      => '#676A70', | ||||
| @@ -208,9 +207,6 @@ final class CelerityDefaultPostprocessor | ||||
|       // Usually light yellow | ||||
|       'gentle.highlight' => '#fdf3da', | ||||
|       'gentle.highlight.border' => '#c9b8a8', | ||||
|       'gentle.highlight.background' => '#fffdf6', | ||||
|  | ||||
|       'highlight.bright' => '#fdf320', | ||||
|  | ||||
|       'paste.content' => '#fffef5', | ||||
|       'paste.border' => '#e9dbcd', | ||||
| @@ -244,9 +240,6 @@ final class CelerityDefaultPostprocessor | ||||
|  | ||||
|       'document.border' => '#dedee1', | ||||
|  | ||||
|       'delete-color' => '#c0392b', | ||||
|       'create-color' => '#139543', | ||||
|  | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -17,9 +17,6 @@ final class CelerityRedGreenPostprocessor | ||||
|       'new-bright' => 'rgba(152, 207, 235, .35)', | ||||
|       'old-background' => 'rgba(250, 212, 175, .3)', | ||||
|       'old-bright' => 'rgba(250, 212, 175, .55)', | ||||
|  | ||||
|       'delete-color' => '#e67e22', | ||||
|       'create-color' => '#2980b9', | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -100,17 +100,9 @@ final class PhabricatorConduitAPIController | ||||
|       } | ||||
|     } catch (Exception $ex) { | ||||
|       $result = null; | ||||
|  | ||||
|       if ($ex instanceof ConduitException) { | ||||
|         $error_code = 'ERR-CONDUIT-CALL'; | ||||
|       } else { | ||||
|         $error_code = 'ERR-CONDUIT-CORE'; | ||||
|  | ||||
|         // See T13581. When a Conduit method raises an uncaught exception | ||||
|         // other than a "ConduitException", log it. | ||||
|         phlog($ex); | ||||
|       } | ||||
|  | ||||
|       $error_code = ($ex instanceof ConduitException | ||||
|         ? 'ERR-CONDUIT-CALL' | ||||
|         : 'ERR-CONDUIT-CORE'); | ||||
|       $error_info = $ex->getMessage(); | ||||
|     } | ||||
|  | ||||
| @@ -142,17 +134,9 @@ final class PhabricatorConduitAPIController | ||||
|           $method_implementation); | ||||
|       case 'json': | ||||
|       default: | ||||
|         $response = id(new AphrontJSONResponse()) | ||||
|         return id(new AphrontJSONResponse()) | ||||
|           ->setAddJSONShield(false) | ||||
|           ->setContent($response->toDictionary()); | ||||
|  | ||||
|         $capabilities = $this->getConduitCapabilities(); | ||||
|         if ($capabilities) { | ||||
|           $capabilities = implode(' ', $capabilities); | ||||
|           $response->addHeader('X-Conduit-Capabilities', $capabilities); | ||||
|         } | ||||
|  | ||||
|         return $response; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -732,14 +716,5 @@ final class PhabricatorConduitAPIController | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   private function getConduitCapabilities() { | ||||
|     $capabilities = array(); | ||||
|  | ||||
|     if (AphrontRequestStream::supportsGzip()) { | ||||
|       $capabilities[] = 'gzip'; | ||||
|     } | ||||
|  | ||||
|     return $capabilities; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||