1<?php 2/** 3 * This file is part of the FreeDSx LDAP package. 4 * 5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace FreeDSx\Ldap\Operation\Request; 12 13use FreeDSx\Asn1\Asn1; 14use FreeDSx\Asn1\Type\AbstractType; 15use FreeDSx\Asn1\Type\SequenceType; 16use FreeDSx\Ldap\Exception\ProtocolException; 17use FreeDSx\Ldap\Protocol\LdapEncoder; 18use FreeDSx\Ldap\Protocol\ProtocolElementInterface; 19 20/** 21 * An Extended Request. RFC 4511, 4.12 22 * 23 * ExtendedRequest ::= [APPLICATION 23] SEQUENCE { 24 * requestName [0] LDAPOID, 25 * requestValue [1] OCTET STRING OPTIONAL } 26 * 27 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 28 */ 29class ExtendedRequest implements RequestInterface 30{ 31 protected const APP_TAG = 23; 32 33 /** 34 * Represents a request to cancel an operation. RFC 3909. 35 */ 36 public const OID_CANCEL = '1.3.6.1.1.8'; 37 38 /** 39 * Represents a request to issue a StartTLS to encrypt the connection. 40 */ 41 public const OID_START_TLS = '1.3.6.1.4.1.1466.20037'; 42 43 /** 44 * Represents a "whoami" request. RFC 4532. 45 */ 46 public const OID_WHOAMI = '1.3.6.1.4.1.4203.1.11.3'; 47 48 /** 49 * Represents a Password Modify Extended Operation. RFC 3062. 50 */ 51 public const OID_PWD_MODIFY = '1.3.6.1.4.1.4203.1.11.1'; 52 53 /** 54 * @var string 55 */ 56 protected $requestName; 57 58 /** 59 * @var null|AbstractType|ProtocolElementInterface|string 60 */ 61 protected $requestValue; 62 63 /** 64 * @param string $requestName 65 * @param null|AbstractType|ProtocolElementInterface|string $requestValue 66 */ 67 public function __construct(string $requestName, $requestValue = null) 68 { 69 $this->requestName = $requestName; 70 $this->requestValue = $requestValue; 71 } 72 73 /** 74 * @param string $requestName 75 * @return $this 76 */ 77 public function setName(string $requestName) 78 { 79 $this->requestName = $requestName; 80 81 return $this; 82 } 83 84 /** 85 * @return string 86 */ 87 public function getName(): string 88 { 89 return $this->requestName; 90 } 91 92 /** 93 * @param AbstractType|ProtocolElementInterface|string|null $requestValue 94 * @return $this 95 */ 96 public function setValue($requestValue) 97 { 98 $this->requestValue = $requestValue; 99 100 return $this; 101 } 102 103 /** 104 * @return AbstractType|ProtocolElementInterface|string|null 105 */ 106 public function getValue() 107 { 108 return $this->requestValue; 109 } 110 111 /** 112 * {@inheritdoc} 113 */ 114 public function toAsn1(): AbstractType 115 { 116 $asn1 = Asn1::sequence(Asn1::context(0, Asn1::octetString($this->requestName))); 117 118 if ($this->requestValue !== null) { 119 $value = $this->requestValue; 120 $encoder = new LdapEncoder(); 121 if ($value instanceof AbstractType) { 122 $value = $encoder->encode($value); 123 } elseif ($value instanceof ProtocolElementInterface) { 124 $value = $encoder->encode($value->toAsn1()); 125 } 126 $asn1->addChild(Asn1::context(1, Asn1::octetString($value))); 127 } 128 129 return Asn1::application(self::APP_TAG, $asn1); 130 } 131 132 /** 133 * {@inheritdoc} 134 */ 135 public static function fromAsn1(AbstractType $type) 136 { 137 return new self(...self::parseAsn1ExtendedRequest($type)); 138 } 139 140 /** 141 * @param AbstractType $type 142 * @return AbstractType 143 * @throws ProtocolException 144 */ 145 protected static function decodeEncodedValue(AbstractType $type): ?AbstractType 146 { 147 [1 => $value] = self::parseAsn1ExtendedRequest($type); 148 149 return $value !== null ? (new LdapEncoder())->decode($value) : null; 150 } 151 152 /** 153 * @param AbstractType $type 154 * @return array 155 * @throws ProtocolException 156 */ 157 protected static function parseAsn1ExtendedRequest(AbstractType $type) 158 { 159 if (!($type instanceof SequenceType && (\count($type) === 1 || \count($type) === 2))) { 160 throw new ProtocolException('The extended request is malformed'); 161 } 162 $oid = null; 163 $value = null; 164 165 foreach ($type->getChildren() as $child) { 166 if ($child->getTagClass() === AbstractType::TAG_CLASS_CONTEXT_SPECIFIC && $child->getTagNumber() === 0) { 167 $oid = $child; 168 } elseif ($child->getTagClass() === AbstractType::TAG_CLASS_CONTEXT_SPECIFIC && $child->getTagNumber() === 1) { 169 $value = $child; 170 } 171 } 172 if ($oid === null) { 173 throw new ProtocolException('The extended request is malformed'); 174 } 175 if ($value !== null) { 176 $value = $value->getValue(); 177 } 178 179 return [$oid->getValue(), $value]; 180 } 181} 182