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
14 namespace OCA\Grauphel\Controller;
16 use \OCP\AppFramework\Controller;
17 use \OCP\AppFramework\Http\JSONResponse;
19 use \OCA\Grauphel\Lib\NoteStorage;
20 use \OCA\Grauphel\Lib\OAuth;
21 use \OCA\Grauphel\Lib\OAuthException;
22 use \OCA\Grauphel\Lib\Dependencies;
23 use \OCA\Grauphel\Lib\Response\ErrorResponse;
30 * @author Christian Weiske <cweiske@cweiske.de>
31 * @copyright 2014 Christian Weiske
32 * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3
33 * @version Release: @package_version@
34 * @link http://cweiske.de/grauphel.htm
36 class ApiController extends Controller
39 * constructor of the controller
41 * @param string $appName Name of the app
42 * @param IRequest $request Instance of the request
44 public function __construct($appName, \OCP\IRequest $request, $user)
46 parent::__construct($appName, $request);
48 $this->deps = Dependencies::get();
49 $this->notes = new NoteStorage($this->deps->urlGen);
51 //default http header: we assume something is broken
52 header('HTTP/1.0 500 Internal Server Error');
62 public function index($route = 'grauphel.api.index')
64 $deps = Dependencies::get();
65 $authenticated = false;
67 $oauth->setDeps($deps);
68 $urlGen = $deps->urlGen;
71 $provider = OAuth::getProvider();
72 $oauth->registerHandler($provider)
73 ->registerAccessTokenHandler($provider);
74 $provider->checkOAuthRequest(
75 $urlGen->getAbsoluteURL(
76 $urlGen->linkToRoute($route)
79 $authenticated = true;
80 $token = $deps->tokens->load('access', $provider->token);
81 $username = $token->user;
83 } catch (OAuthException $e) {
84 return new ErrorResponse($e->getMessage());
85 } catch (\OAuthException $e) {
86 if ($e->getCode() != OAUTH_PARAMETER_ABSENT) {
89 if ($this->user !== null) {
90 $username = $this->user->getUID();
91 $authenticated = true;
96 'oauth_request_token_url' => $urlGen->getAbsoluteURL(
97 $urlGen->linkToRoute('grauphel.oauth.requestToken')
99 'oauth_authorize_url' => $urlGen->getAbsoluteURL(
100 $urlGen->linkToRoute('grauphel.oauth.authorize')
102 'oauth_access_token_url' => $urlGen->getAbsoluteURL(
103 $urlGen->linkToRoute('grauphel.oauth.accessToken')
105 'api-version' => '1.0',
108 $client = $this->getClient();
109 if ($client !== false) {
110 $data['oauth_authorize_url'] .= '?client='
111 . urlencode($this->getNiceClientName($client));
114 if ($authenticated) {
115 $data['user-ref'] = array(
116 'api-ref' => $urlGen->getAbsoluteURL(
117 $urlGen->linkToRoute(
118 'grauphel.api.user', array('username' => $username)
121 'href' => null,//FIXME
125 return new JSONResponse($data);
135 public function indexSlash()
137 return $this->index('grauphel.api.indexSlash');
147 public function user($username)
151 $this->deps->urlGen->getAbsoluteURL(
152 $this->deps->urlGen->linkToRoute(
153 'grauphel.api.user', array('username' => $username)
157 $syncdata = $this->notes->loadSyncData();
160 'user-name' => $username,
161 'first-name' => null,
163 'notes-ref' => array(
164 'api-ref' => $this->deps->urlGen->getAbsoluteURL(
165 $this->deps->urlGen->linkToRoute(
166 'grauphel.api.notes', array('username' => $username)
171 'latest-sync-revision' => $syncdata->latestSyncRevision,
172 'current-sync-guid' => $syncdata->currentSyncGuid,
174 return new JSONResponse($data);
178 * GET /api/1.0/$user/notes
184 public function notes($username)
188 $this->deps->urlGen->getAbsoluteURL(
189 $this->deps->urlGen->linkToRoute(
190 'grauphel.api.notes', array('username' => $username)
194 $syncdata = $this->notes->loadSyncData();
195 return $this->fetchNotes($syncdata);
199 * PUT /api/1.0/$user/notes
205 public function notesSave($username)
209 $this->deps->urlGen->getAbsoluteURL(
210 $this->deps->urlGen->linkToRoute(
211 'grauphel.api.notesSave', array('username' => $username)
215 $syncdata = $this->notes->loadSyncData();
217 $res = $this->handleNoteSave($username, $syncdata);
218 if ($res instanceof \OCP\AppFramework\Http\Response) {
222 return $this->fetchNotes($syncdata);
225 protected function fetchNotes($syncdata)
228 if (isset($_GET['since'])) {
229 $since = (int) $_GET['since'];
232 if (isset($_GET['include_notes']) && $_GET['include_notes']) {
233 $notes = $this->notes->loadNotesFull($since);
235 $notes = $this->notes->loadNotesOverview($since);
238 //work around bug https://bugzilla.gnome.org/show_bug.cgi?id=734313
239 foreach ($notes as $note) {
240 if (isset($note->{'note-content-version'})) {
241 $note->{'note-content-version'} = 0.3;
246 'latest-sync-revision' => $syncdata->latestSyncRevision,
249 return new JSONResponse($data);
252 protected function handleNoteSave($username, $syncdata)
254 if ($_SERVER['REQUEST_METHOD'] != 'PUT') {
258 //note that we have more data in $arPut than just our JSON
259 // request object merges it with other data
260 $arPut = $this->request->put;
262 //structural validation
263 if (!isset($arPut['latest-sync-revision'])) {
264 return new ErrorResponse('Missing "latest-sync-revision"');
266 if (!isset($arPut['note-changes'])) {
267 return new ErrorResponse('Missing "note-changes"');
269 foreach ($arPut['note-changes'] as $note) {
270 //owncloud converts object to array, so we reverse
271 $note = (object) $note;
272 if (!isset($note->guid) || $note->guid == '') {
273 return new ErrorResponse('Missing "guid" on note');
278 if ($arPut['latest-sync-revision'] != $syncdata->latestSyncRevision +1
279 && $syncdata->latestSyncRevision != -1
281 return new ErrorResponse(
282 'Wrong "latest-sync-revision". You are not up to date.'
287 ++$syncdata->latestSyncRevision;
288 foreach ($arPut['note-changes'] as $noteUpdate) {
289 //owncloud converts object to array, so we reverse
290 $noteUpdate = (object) $noteUpdate;
292 $note = $this->notes->load($noteUpdate->guid);
293 if (isset($noteUpdate->command) && $noteUpdate->command == 'delete') {
294 $this->notes->delete($noteUpdate->guid);
296 $this->notes->update(
297 $note, $noteUpdate, $syncdata->latestSyncRevision
299 $this->notes->save($note);
303 $this->notes->saveSyncData($syncdata);
307 * GET /api/1.0/$user/notes/$noteguid
313 public function note($username, $guid)
317 $this->deps->urlGen->getAbsoluteURL(
318 $this->deps->urlGen->linkToRoute(
320 array('username' => $username, 'guid' => $guid)
325 $note = $this->notes->load($guid, false);
326 if ($note === null) {
327 header('HTTP/1.0 404 Not Found');
328 header('Content-type: text/plain');
329 echo "Note does not exist\n";
333 return new JSONResponse($note);
336 protected function getClient()
338 if (isset($_SERVER['HTTP_X_TOMBOY_CLIENT'])) {
339 $client = $_SERVER['HTTP_X_TOMBOY_CLIENT'];
340 $doublepos = strpos($client, ', org.tomdroid');
341 if ($doublepos !== false) {
342 //https://bugs.launchpad.net/tomdroid/+bug/1375436
343 //X-Tomboy-Client header is sent twice
344 $client = substr($client, 0, $doublepos);
352 protected function getNiceClientName($client)
354 if (substr($client, 0, 12) == 'org.tomdroid') {
355 //org.tomdroid v0.7.5, build 14, Android v4.4.2, innotek GmbH/VirtualBox
362 * Checks if the given user is authorized (by oauth token or normal login)
364 * @param string $username Username to verify
366 * @return boolean True if all is fine, Response in case of an error
368 protected function verifyUser($username, $curUrl)
370 if ($this->user !== null && $this->user->getUid() == $username) {
371 $this->notes->setUsername($username);
375 $oauth = new OAuth();
376 $oauth->setDeps($this->deps);
377 $oauth->verifyOAuthUser($username, $curUrl);
379 $this->notes->setUsername($username);