Adds 2017 to copyrights
[src/app-framework-binder.git] / bindings / media / media-api.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author "Manuel Bachmann"
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <string.h>
21
22 #include <json-c/json.h>
23
24 #include "media-api.h"
25 #include "media-rygel.h"
26
27 #include <afb/afb-binding.h>
28 #include <afb/afb-req-itf.h>
29
30 json_object* _rygel_list (mediaCtxHandleT *);
31
32 /* ------ LOCAL HELPER FUNCTIONS --------- */
33
34 /* private client context creation ; default values */
35 static mediaCtxHandleT* initMediaCtx () {
36
37     mediaCtxHandleT *ctx;
38
39     ctx = malloc (sizeof(mediaCtxHandleT));
40     ctx->media_server = NULL;
41     ctx->index = 0;
42
43     return ctx;
44 }
45
46 /* ------ PUBLIC PLUGIN FUNCTIONS --------- */
47
48 static void init (struct afb_req request) {        /* AFB_SESSION_CHECK */
49
50     mediaCtxHandleT *ctx = afb_req_context_get(request);
51     json_object *jresp;
52
53     /* create a private client context */
54     if (!ctx) {
55         ctx = initMediaCtx();
56         afb_req_context_set (request, ctx, free);
57     }
58
59     /* initialize server connection */
60     if (!ctx->media_server)
61       _rygel_init (ctx);
62
63     jresp = json_object_new_object ();
64     json_object_object_add (jresp, "init", json_object_new_string ("success"));
65     afb_req_success (request, jresp, "Media - Initialized");
66 }
67
68 static void list (struct afb_req request) {        /* AFB_SESSION_CHECK */
69
70     mediaCtxHandleT *ctx = afb_req_context_get(request);
71     json_object *jresp;
72
73     /* check that context is initialized */
74     if (ctx == NULL) {
75       afb_req_fail (request, "failed", "uninitialized");
76       return;
77     }
78
79     jresp = _rygel_list (ctx);
80
81     if (!jresp) {
82       afb_req_fail (request, "failed", "no content found in media server");
83       return;
84     }
85
86     afb_req_success (request, jresp, "Media - Listed");
87 }
88
89 static void selecting (struct afb_req request) {   /* AFB_SESSION_CHECK */
90
91     mediaCtxHandleT *ctx = afb_req_context_get(request);
92     const char *value = afb_req_value (request, "value");
93     json_object *jresp;
94     unsigned int index;
95     char index_str[5];
96
97     /* check that context is initialized */
98     if (ctx == NULL) {
99       afb_req_fail (request, "failed", "uninitialized");
100       return;
101     }
102
103     /* no "?value=" parameter : return current index */
104     if (!value) {
105         snprintf (index_str, sizeof(index_str), "%d", ctx->index);
106         jresp = json_object_new_object();
107         json_object_object_add (jresp, "index", json_object_new_string (index_str));
108     }
109
110     /* "?value=" parameter is negative */
111     else if (atoi(value) < 0) {
112         afb_req_fail (request, "failed", "chosen index cannot be negative");
113         return;
114     }
115
116     /* "?value=" parameter is positive */
117     else if (atoi(value) >= 0) {
118         index = (unsigned int) atoi(value);
119
120         if (!_rygel_select (ctx, index)) {
121           afb_req_fail (request, "failed", "chosen index superior to current media count");
122           return;
123         }
124
125         ctx->index = index;
126         jresp = json_object_new_object();
127         json_object_object_add (jresp, "index", json_object_new_string (value));
128     }
129     else
130         jresp = NULL;
131
132     afb_req_success (request, jresp, "Media - Listed");
133 }
134
135 static void play (struct afb_req request) {        /* AFB_SESSION_CHECK */
136
137     mediaCtxHandleT *ctx = afb_req_context_get(request);
138     json_object *jresp;
139
140     /* check that context is initialized */
141     if (ctx == NULL) {
142       afb_req_fail (request, "failed", "uninitialized");
143       return;
144     }
145
146     if (!_rygel_do (ctx, PLAY, NULL)) {
147       afb_req_fail (request, "failed", "could not play chosen media");
148       return;
149     }
150
151     jresp = json_object_new_object ();
152     json_object_object_add (jresp, "play", json_object_new_string ("success"));
153     afb_req_success (request, jresp, "Media - Listed");
154 }
155
156 static void stop (struct afb_req request) {        /* AFB_SESSION_CHECK */
157
158     mediaCtxHandleT *ctx = afb_req_context_get(request);
159     json_object *jresp;
160
161     /* check that context is initialized */
162     if (ctx == NULL) {
163       afb_req_fail (request, "failed", "uninitialized");
164       return;
165     }
166
167     if (!_rygel_do (ctx, STOP, NULL)) {
168       afb_req_fail (request, "failed", "could not stop chosen media");
169       return;
170     }
171
172     jresp = json_object_new_object ();
173     json_object_object_add (jresp, "stop", json_object_new_string ("success"));
174     afb_req_success (request, jresp, "Media - Stopped");
175 }
176
177 static void pausing (struct afb_req request) {     /* AFB_SESSION_CHECK */
178
179     mediaCtxHandleT *ctx = afb_req_context_get(request);
180     json_object *jresp;
181
182     /* check that context is initialized */
183     if (ctx == NULL) {
184       afb_req_fail (request, "failed", "uninitialized");
185       return;
186     }
187
188     if (!_rygel_do (ctx, PAUSE, NULL)) {
189       afb_req_fail (request, "failed", "could not pause chosen media");
190       return;
191     }
192
193     jresp = json_object_new_object();
194     json_object_object_add (jresp, "pause", json_object_new_string ("success"));
195     afb_req_success (request, jresp, "Media - Paused");
196 }
197
198 static void seek (struct afb_req request) {        /* AFB_SESSION_CHECK */
199
200     mediaCtxHandleT *ctx = afb_req_context_get(request);
201     const char *value = afb_req_value (request, "value");
202     json_object *jresp;
203
204     /* check that context is initialized */
205     if (ctx == NULL) {
206       afb_req_fail (request, "failed", "uninitialized");
207       return;
208     }
209
210     /* no "?value=" parameter : return error */
211     if (!value) {
212       afb_req_fail (request, "failed", "you must provide a time");
213       return;
214     }
215
216     if (!_rygel_do (ctx, SEEK, (char *)value)) {
217       afb_req_fail (request, "failed", "could not seek chosen media");
218       return;
219     }
220
221     jresp = json_object_new_object();
222     json_object_object_add (jresp, "seek", json_object_new_string ("success"));
223     afb_req_success (request, jresp, "Media - Sought");
224 }
225
226 static char *renamed_filename(struct afb_arg argfile)
227 {
228     char *result;
229     const char *e = strrchr(argfile.path, '/');
230     if (e == NULL)
231         result = strdup(argfile.value);
232     else {
233         result = malloc((++e - argfile.path) + strlen(argfile.value) + 1);
234         if (result != NULL)
235             strcpy(stpncpy(result, argfile.path, e - argfile.path), argfile.value);
236     }
237     return result;
238 }
239
240 static void on_uploaded(struct afb_req *prequest, int status)
241 {
242     struct afb_req request = afb_req_unstore(prequest);
243     struct afb_arg argfile = afb_req_get(request, "file-upload");
244     char *file = renamed_filename(argfile);
245     if (file != NULL)
246         unlink(file);
247     free(file);
248     if (status)
249         afb_req_fail (request, "failed", "expected file not received");
250     else
251         afb_req_success_f (request, NULL, "uploaded file %s", argfile.value);
252    afb_req_unref(request);
253 }
254
255 static void upload (struct afb_req request) { /* AFB_SESSION_CHECK */
256
257     mediaCtxHandleT *ctx = afb_req_context_get(request);
258     struct afb_req *prequest;
259     struct afb_arg argfile;
260     char *path;
261
262     /* check that context is initialized */
263     if (ctx == NULL) {
264       afb_req_fail (request, "failed", "uninitialized");
265       return;
266     }
267
268     /* get the file */
269     argfile = afb_req_get(request, "file-upload");
270     if (!argfile.value || !argfile.path) {
271         afb_req_fail (request, "failed", "expected file not received");
272         return;
273     }
274
275     /* rename the file */
276     path = renamed_filename(argfile);
277     if (path == NULL) {
278         afb_req_fail (request, "failed", "out of memory");
279         return;
280     }
281     if (rename(argfile.path, path) != 0) {
282         free(path);
283         afb_req_fail (request, "failed", "system error");
284         return;
285     }
286
287     /* for asynchronous processing */
288     prequest = afb_req_store(request);
289     if (path == NULL) {
290         unlink(path);
291         afb_req_fail (request, "failed", "out of memory");
292     }
293     else if (!_rygel_upload (ctx, path, (void*)on_uploaded, prequest)) {
294         afb_req_unref(afb_req_unstore(prequest));
295         unlink(path);
296         afb_req_fail (request, "failed", "Error when uploading file to media server... could not complete");
297     }
298     free(path);
299 }
300
301 static void ping (struct afb_req request) {         /* AFB_SESSION_NONE */
302     afb_req_success (request, NULL, "Media - Ping succeeded");
303 }
304
305
306 static const struct afb_verb_desc_v1 verbs[]= {
307   {"init"   , AFB_SESSION_CHECK,  init       , "Media API - init"   },
308   {"list"   , AFB_SESSION_CHECK,  list       , "Media API - list"   },
309   {"select" , AFB_SESSION_CHECK,  selecting  , "Media API - select" },
310   {"play"   , AFB_SESSION_CHECK,  play       , "Media API - play"   },
311   {"stop"   , AFB_SESSION_CHECK,  stop       , "Media API - stop"   },
312   {"pause"  , AFB_SESSION_CHECK,  pausing    , "Media API - pause"  },
313   {"seek"   , AFB_SESSION_CHECK,  seek       , "Media API - seek"   },
314 //  {"upload" , AFB_SESSION_CHECK,  upload     , "Media API - upload" },
315   {"ping"   , AFB_SESSION_NONE,   ping       , "Media API - ping"   },
316   {NULL}
317 };
318
319 static const struct afb_binding pluginDesc = {
320     .type   = AFB_BINDING_VERSION_1,
321     .v1 = {
322         .info   = "Application Framework Binder - Media plugin",
323         .prefix = "media",
324         .verbs   = verbs
325     }
326 };
327
328 const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf)
329 {
330     return &pluginDesc;
331 }