3d7221dc4cd972b4e0bb00cee103a534e76f4ed6
[shpub.git] / src / shpub / Request.php
1 <?php
2 namespace shpub;
3
4 class Request
5 {
6     public $req;
7     public $cfg;
8     public $host;
9
10     protected $sendAsJson = false;
11     protected $directUpload = false;
12     protected $uploadsInfo = [];
13     protected $dedicatedBody = false;
14
15     protected $properties = [];
16     protected $type = null;
17     protected $action = null;
18     protected $url = null;
19
20     public function __construct($host, $cfg)
21     {
22         $this->cfg  = $cfg;
23         $this->host = $host;
24         $this->req = $this->getHttpRequest(
25             $this->host->endpoints->micropub, $this->host->token
26         );
27     }
28
29     protected function getHttpRequest($url, $accessToken)
30     {
31         $req = new MyHttpRequest2($url, 'POST');
32         $req->setHeader('User-Agent: shpub');
33         if (version_compare(PHP_VERSION, '5.6.0', '<')) {
34             //correct ssl validation on php 5.5 is a pain, so disable
35             $req->setConfig('ssl_verify_host', false);
36             $req->setConfig('ssl_verify_peer', false);
37         }
38         $req->setHeader('Content-type', 'application/x-www-form-urlencoded');
39         $req->setHeader('authorization', 'Bearer ' . $accessToken);
40         return $req;
41     }
42
43     public function send($body = null)
44     {
45         if ($this->sendAsJson) {
46             //application/json
47             if ($body !== null) {
48                 throw new \Exception('body already defined');
49             }
50             $this->req->setHeader('Content-Type: application/json');
51             $data = [];
52             if ($this->action !== null) {
53                 $data['action'] = $this->action;
54             }
55             if ($this->url !== null) {
56                 $data['url'] = $this->url;
57             }
58             if ($this->type !== null) {
59                 $data['type'] = array('h-' . $this->type);
60             }
61             if (count($this->properties)) {
62                 $data['properties'] = $this->properties;
63             }
64             $body = json_encode($data);
65         } else {
66             //form-encoded
67             if ($this->type !== null) {
68                 $this->req->addPostParameter('h', $this->type);
69             }
70             if ($this->action !== null) {
71                 $this->req->addPostParameter('action', $this->action);
72             }
73             if ($this->url !== null) {
74                 $this->req->addPostParameter('url', $this->url);
75             }
76             foreach ($this->properties as $propkey => $propval) {
77                 if (isset($propval['html'])) {
78                     //workaround for content[html]
79                     $propkey = $propkey . '[html]';
80                     $propval = $propval['html'];
81                 } else if (count($propval) == 1) {
82                     $propval = reset($propval);
83                 }
84                 $this->req->addPostParameter($propkey, $propval);
85             }
86         }
87
88         if ($body !== null) {
89             $this->dedicatedBody = true;
90             $this->req->setBody($body);
91         }
92         if ($this->cfg->debug) {
93             $cp = new CurlPrinter();
94             $cp->show($this->req, $this->uploadsInfo, $this->dedicatedBody);
95         }
96         $res = $this->req->send();
97
98         if (intval($res->getStatus() / 100) != 2) {
99             $this->displayErrorResponse($res);
100         }
101         return $res;
102     }
103
104     protected function displayErrorResponse($res)
105     {
106         Log::err(
107             'Server returned an error status code ' . $res->getStatus()
108         );
109
110         $shown = false;
111         if ($res->getHeader('content-type') == 'application/json') {
112             $errData = json_decode($res->getBody());
113             if (!isset($errData->error)) {
114                 Log::err('Error response does not contain "error" property');
115             } else if (isset($errData->error_description)) {
116                 Log::err($errData->error . ': ' . $errData->error_description);
117                 $shown = true;
118             } else {
119                 Log::err($errData->error);
120                 $shown = true;
121             }
122         }
123
124         if (!$shown) {
125             Log::err($res->getBody());
126         }
127         exit(11);
128     }
129
130     public function setAction($action)
131     {
132         $this->action = $action;
133     }
134
135     public function setType($type)
136     {
137         $this->type = $type;
138     }
139
140     public function setUrl($url)
141     {
142         $this->url = $url;
143     }
144
145     /**
146      * Add file upload
147      *
148      * @param string $fieldName name of file-upload field
149      * @param array  $fileNames list of local file paths
150      *
151      * @return void
152      */
153     public function addUpload($fieldName, $fileNames)
154     {
155         if ($this->directUpload && $this->sendAsJson) {
156             throw new \Exception(
157                 'Cannot do direct upload with JSON requests'
158             );
159         }
160
161         if ($this->host->endpoints->media === null
162             || $this->directUpload
163         ) {
164             if ($this->sendAsJson) {
165                 throw new \Exception(
166                     'No media endpoint available, which is required for JSON'
167                 );
168             }
169             if (count($fileNames) == 1) {
170                 $fileNames = reset($fileNames);
171             }
172             $this->uploadsInfo[$fieldName] = $fileNames;
173             return $this->req->addUpload($fieldName, $fileNames);
174         }
175
176         $urls = [];
177         foreach ($fileNames as $fileName) {
178             $urls[] = $this->uploadToMediaEndpoint($fileName);
179         }
180         if (count($urls) == 1) {
181             $urls = reset($urls);
182         }
183         $this->addProperty($fieldName, $urls);
184     }
185
186     /**
187      * Execute the file upload
188      *
189      * @param string $fileName File path
190      *
191      * @return string URL at media endpoint
192      */
193     public function uploadToMediaEndpoint($fileName)
194     {
195         $httpReq = $this->getHttpRequest(
196             $this->host->endpoints->media, $this->host->token
197         );
198         $httpReq->addUpload('file', $fileName);
199
200         if ($this->cfg->debug) {
201             $cp = new CurlPrinter();
202             $cp->show($httpReq, ['file' => $fileName]);
203         }
204         $res = $httpReq->send();
205         if (intval($res->getStatus() / 100) != 2) {
206             Log::err(
207                 'Media endpoint returned an error status code '
208                 . $res->getStatus()
209             );
210             Log::err($res->getBody());
211             exit(11);
212         }
213
214         $location = $res->getHeader('location');
215         if ($location === null) {
216             Log::err('Media endpoint did not return a URL');
217             exit(11);
218         }
219
220         $base = new \Net_URL2($this->host->endpoints->media);
221         return (string) $base->resolve($location);
222     }
223
224     public function addContent($text, $isHtml)
225     {
226         if ($isHtml) {
227             $this->addProperty(
228                 'content', [['html' => $text]]
229             );
230         } else {
231             $this->addProperty('content', $text);
232         }
233
234     }
235
236     /**
237      * Adds a micropub property to the request.
238      *
239      * @param string       $key    Parameter name
240      * @param string|array $values One or multiple values
241      *
242      * @return void
243      */
244     public function addProperty($key, $values)
245     {
246         $this->properties[$key] = (array) $values;
247     }
248
249     public function setSendAsJson($json)
250     {
251         $this->sendAsJson = $json;
252     }
253
254     public function setDirectUpload($directUpload)
255     {
256         $this->directUpload = $directUpload;
257     }
258 }