166436e1b2e5cde68e45105a0d42119e75a0b774
[grauphel.git] / controller / apicontroller.php
1 <?php
2 /**
3  * Part of grauphel
4  *
5  * PHP version 5
6  *
7  * @category  Tools
8  * @package   Grauphel
9  * @author    Christian Weiske <cweiske@cweiske.de>
10  * @copyright 2014 Christian Weiske
11  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
12  * @link      http://cweiske.de/grauphel.htm
13  */
14 namespace OCA\Grauphel\Controller;
15
16 use \OCP\AppFramework\Controller;
17 use \OCP\AppFramework\Http\JSONResponse;
18
19 use \OCA\Grauphel\Lib\NoteStorage;
20 use \OCA\Grauphel\Lib\OAuth;
21 use \OCA\Grauphel\Lib\Dependencies;
22
23 /**
24  * Tomboy's REST API
25  *
26  * @category  Tools
27  * @package   Grauphel
28  * @author    Christian Weiske <cweiske@cweiske.de>
29  * @copyright 2014 Christian Weiske
30  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
31  * @version   Release: @package_version@
32  * @link      http://cweiske.de/grauphel.htm
33  */
34 class ApiController extends Controller
35 {
36         /**
37          * constructor of the controller
38      *
39          * @param string   $appName Name of the app
40          * @param IRequest $request Instance of the request
41          */
42         public function __construct($appName, \OCP\IRequest $request, $user)
43     {
44         parent::__construct($appName, $request);
45         $this->user  = $user;
46         $this->deps  = Dependencies::get();
47         $this->notes = new NoteStorage($this->deps->urlGen);
48
49         //default http header: we assume something is broken
50         header('HTTP/1.0 500 Internal Server Error');
51     }
52
53     /**
54      * /api/1.0
55      *
56      * @NoAdminRequired
57      * @NoCSRFRequired
58      * @PublicPage
59      */
60     public function index($route = 'grauphel.api.index')
61     {
62         $deps = Dependencies::get();
63         $authenticated = false;
64         $oauth = new OAuth();
65         $oauth->setDeps($deps);
66         $urlGen = $deps->urlGen;
67
68         try {
69             $provider = new \OAuthProvider();
70             $oauth->registerHandler($provider)
71                 ->registerAccessTokenHandler($provider);
72             $provider->checkOAuthRequest(
73                 $urlGen->getAbsoluteURL(
74                     $urlGen->linkToRoute($route)
75                 )
76             );
77             $authenticated = true;
78             $token = $deps->tokens->load('access', $provider->token);
79             $username = $token->user;
80
81         } catch (\OAuth_Exception $e) {
82             $deps->renderer->errorOut($e->getMessage());
83         } catch (\OAuthException $e) {
84             if ($e->getCode() != OAUTH_PARAMETER_ABSENT) {
85                 $oauth->error($e);
86             }
87             if ($this->user !== null) {
88                 $username = $this->user->getUID();
89                 $authenticated = true;
90             }
91         }
92
93         $data = array(
94             'oauth_request_token_url' => $urlGen->getAbsoluteURL(
95                 $urlGen->linkToRoute('grauphel.oauth.requestToken')
96             ),
97             'oauth_authorize_url'     => $urlGen->getAbsoluteURL(
98                 $urlGen->linkToRoute('grauphel.oauth.authorize')
99             ),
100             'oauth_access_token_url'  => $urlGen->getAbsoluteURL(
101                 $urlGen->linkToRoute('grauphel.oauth.accessToken')
102             ),
103             'api-version' => '1.0',
104         );
105
106         if ($authenticated) {
107             $data['user-ref'] = array(
108                 'api-ref' => $urlGen->getAbsoluteURL(
109                     $urlGen->linkToRoute(
110                         'grauphel.api.user', array('username' => $username)
111                     )
112                 ),
113                 'href' => null,//FIXME
114             );
115         }
116
117         return new JSONResponse($data);
118     }
119
120     /**
121      * /api/1.0/
122      *
123      * @NoAdminRequired
124      * @NoCSRFRequired
125      * @PublicPage
126      */
127     public function indexSlash()
128     {
129         return $this->index('grauphel.api.indexSlash');
130     }
131
132     /**
133      * GET /api/1.0/$user
134      *
135      * @NoAdminRequired
136      * @NoCSRFRequired
137      * @PublicPage
138      */
139     public function user($username)
140     {
141         $this->verifyUser($username);
142         $syncdata = $this->notes->loadSyncData($username);
143
144         $data = array(
145             'user-name'  => $username,
146             'first-name' => null,
147             'last-name'  => null,
148             'notes-ref'  => array(
149                 'api-ref' => $this->deps->urlGen->getAbsoluteURL(
150                     $this->deps->urlGen->linkToRoute(
151                         'grauphel.api.notes', array('username' => $username)
152                     )
153                 ),
154                 'href'    => null,
155             ),
156             'latest-sync-revision' => $syncdata->latestSyncRevision,
157             'current-sync-guid'    => $syncdata->currentSyncGuid,
158         );
159         return new JSONResponse($data);
160     }
161
162     /**
163      * GET /api/1.0/$user/notes
164      *
165      * @NoAdminRequired
166      * @NoCSRFRequired
167      * @PublicPage
168      */
169     public function notes($username)
170     {
171         $this->verifyUser(
172             $username,
173             $this->deps->urlGen->getAbsoluteURL(
174                 $this->deps->urlGen->linkToRoute(
175                     'grauphel.api.notes', array('username' => $username)
176                 )
177             )
178         );
179         $syncdata = $this->notes->loadSyncData($username);
180         return $this->fetchNotes($username, $syncdata);
181     }
182
183     /**
184      * PUT /api/1.0/$user/notes
185      *
186      * @NoAdminRequired
187      * @NoCSRFRequired
188      * @PublicPage
189      */
190     public function notesSave($username)
191     {
192         $this->verifyUser(
193             $username,
194             $this->deps->urlGen->getAbsoluteURL(
195                 $this->deps->urlGen->linkToRoute(
196                     'grauphel.api.user', array('username' => $username)
197                 )
198             )
199         );
200         $syncdata = $this->notes->loadSyncData($username);
201         
202         $this->handleNoteSave($username, $syncdata);
203
204         return $this->fetchNotes($username, $syncdata);
205     }
206
207     protected function fetchNotes($username, $syncdata)
208     {
209         $since = null;
210         if (isset($_GET['since'])) {
211             $since = (int) $_GET['since'];
212         }
213
214         if (isset($_GET['include_notes']) && $_GET['include_notes']) {
215             $notes = $this->notes->loadNotesFull($username, $since);
216         } else {
217             $notes = $this->notes->loadNotesOverview($username, $since);
218         }
219
220         //work around bug https://bugzilla.gnome.org/show_bug.cgi?id=734313
221         foreach ($notes as $note) {
222             if (isset($note->{'note-content-version'})) {
223                 $note->{'note-content-version'} = 0.3;
224             }
225         }
226
227         $data = array(
228             'latest-sync-revision' => $syncdata->latestSyncRevision,
229             'notes' => $notes,
230         );
231         return new JSONResponse($data);
232     }
233
234     protected function handleNoteSave($username, $syncdata)
235     {
236         if ($_SERVER['REQUEST_METHOD'] != 'PUT') {
237             return;
238         }
239
240         $data = file_get_contents('php://input');
241         $putObj = json_decode($data);
242         if ($putObj === NULL) {
243             errorOut('Invalid JSON data in PUT request');
244         }
245
246         //structural validation
247         if (!isset($putObj->{'latest-sync-revision'})) {
248             errorOut('Missing "latest-sync-revision"');
249         }
250         if (!isset($putObj->{'note-changes'})) {
251             errorOut('Missing "note-changes"');
252         }
253         foreach ($putObj->{'note-changes'} as $note) {
254             if (!isset($note->guid) || $note->guid == '') {
255                 errorOut('Missing "guid" on note');
256             }
257         }
258
259         //content validation
260         if ($putObj->{'latest-sync-revision'} != $syncdata->latestSyncRevision +1
261             && $syncdata->latestSyncRevision != -1
262         ) {
263             errorOut('Wrong "latest-sync-revision". You are not up to date.');
264         }
265
266         //update
267         $deps = Dependencies::get();
268         ++$syncdata->latestSyncRevision;
269         foreach ($putObj->{'note-changes'} as $noteUpdate) {
270             $note = $deps->notes->load($username, $noteUpdate->guid);
271             if (isset($noteUpdate->command) && $noteUpdate->command == 'delete') {
272                 $deps->notes->delete($username, $noteUpdate->guid);
273             } else {
274                 $deps->notes->update(
275                     $note, $noteUpdate, $syncdata->latestSyncRevision
276                 );
277                 $deps->notes->save($username, $note);
278             }
279         }
280
281         $deps->notes->saveSyncData($username, $syncdata);
282     }
283
284     /**
285      * GET /api/1.0/$user/notes/$noteguid
286      *
287      * @NoAdminRequired
288      * @NoCSRFRequired
289      * @PublicPage
290      */
291     public function note()
292     {
293         //FIXME
294         $deps = Dependencies::get();
295         $username = $deps->urlGen->loadUsername();
296         $guid     = $deps->urlGen->loadGuid();
297         $oauth = new \OAuth();
298         $oauth->setDeps($deps);
299         $oauth->verifyOAuthUser($username, $deps->urlGen->note($username, $guid));
300
301         $note = $deps->notes->load($username, $guid, false);
302         if ($note === null) {
303             header('HTTP/1.0 404 Not Found');
304             header('Content-type: text/plain');
305             echo "Note does not exist\n";
306             exit(1);
307         }
308
309         $data = array('note' => array($note));
310         $deps->renderer->sendJson($data);
311     }
312
313     /**
314      * Checks if the given user is authorized (by oauth token or normal login)
315      *
316      * @param string $username Username to verify
317      *
318      * @return boolean True if all is fine, Response in case of an error
319      */
320     protected function verifyUser($username, $curUrl)
321     {
322         if ($this->user !== null && $this->user->getUID() == $username) {
323             return true;
324         }
325
326         $oauth = new OAuth();
327         $oauth->setDeps($this->deps);
328         $oauth->verifyOAuthUser($username, $curUrl);
329     }
330 }
331 ?>