1 /*
2  *  API - Common functions for control/query API
3  *
4  *  Copyright (C) 2013 Adam Sutton
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "tvheadend.h"
21 #include "api.h"
22 #include "access.h"
23 
24 #include <string.h>
25 
26 typedef struct api_link {
27   const api_hook_t   *hook;
28   RB_ENTRY(api_link)  link;
29 } api_link_t;
30 
31 RB_HEAD(,api_link) api_hook_tree;
32 SKEL_DECLARE(api_skel, api_link_t);
33 
ah_cmp(api_link_t * a,api_link_t * b)34 static int ah_cmp
35   ( api_link_t *a, api_link_t *b )
36 {
37   return strcmp(a->hook->ah_subsystem, b->hook->ah_subsystem);
38 }
39 
40 void
api_register(const api_hook_t * hook)41 api_register ( const api_hook_t *hook )
42 {
43   api_link_t *t;
44   SKEL_ALLOC(api_skel);
45   api_skel->hook = hook;
46   t = RB_INSERT_SORTED(&api_hook_tree, api_skel, link, ah_cmp);
47   if (t) {
48     tvherror(LS_API, "trying to re-register subsystem");
49   } else {
50     SKEL_USED(api_skel);
51   }
52 }
53 
54 void
api_register_all(const api_hook_t * hooks)55 api_register_all ( const api_hook_t *hooks )
56 {
57   while (hooks->ah_subsystem) {
58     api_register(hooks);
59     hooks++;
60   }
61 }
62 
63 int
api_exec(access_t * perm,const char * subsystem,htsmsg_t * args,htsmsg_t ** resp)64 api_exec ( access_t *perm, const char *subsystem,
65            htsmsg_t *args, htsmsg_t **resp )
66 {
67   api_hook_t h;
68   api_link_t *ah, skel;
69   const char *op;
70 
71   /* Args and response must be set */
72   if (!args || !resp || !subsystem)
73     return EINVAL;
74 
75   // Note: there is no locking while checking the hook tree, its assumed
76   //       this is all setup during init (if this changes the code will
77   //       need updating)
78   h.ah_subsystem = subsystem;
79   skel.hook      = &h;
80   ah = RB_FIND(&api_hook_tree, &skel, link, ah_cmp);
81 
82   if (!ah) {
83     tvhwarn(LS_API, "failed to find subsystem [%s]", subsystem);
84     return ENOSYS; // TODO: is this really the right error code?
85   }
86 
87   if (access_verify2(perm, ah->hook->ah_access))
88     return EPERM;
89 
90   /* Extract method */
91   op = htsmsg_get_str(args, "method");
92   if (!op)
93     op = htsmsg_get_str(args, "op");
94   // Note: this is not required (so no final validation)
95 
96   /* Execute */
97   return ah->hook->ah_callback(perm, ah->hook->ah_opaque, op, args, resp);
98 }
99 
100 static int
api_serverinfo(access_t * perm,void * opaque,const char * op,htsmsg_t * args,htsmsg_t ** resp)101 api_serverinfo
102   ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
103 {
104   *resp = htsmsg_create_map();
105   htsmsg_add_str(*resp, "sw_version",   tvheadend_version);
106   htsmsg_add_u32(*resp, "api_version",  TVH_API_VERSION);
107   htsmsg_add_str(*resp, "name",         "Tvheadend");
108   if (tvheadend_webroot)
109     htsmsg_add_str(*resp, "webroot",      tvheadend_webroot);
110   htsmsg_add_msg(*resp, "capabilities", tvheadend_capabilities_list(1));
111   return 0;
112 }
113 
api_init(void)114 void api_init ( void )
115 {
116   static api_hook_t h[] = {
117     { "serverinfo", ACCESS_ANONYMOUS, api_serverinfo, NULL },
118     { NULL, 0, NULL, NULL }
119   };
120   api_register_all(h);
121 
122   /* Subsystems */
123   api_idnode_init();
124   api_config_init();
125   api_input_init();
126   api_mpegts_init();
127   api_service_init();
128   api_channel_init();
129   api_bouquet_init();
130   api_epg_init();
131   api_epggrab_init();
132   api_status_init();
133   api_imagecache_init();
134   api_esfilter_init();
135   api_intlconv_init();
136   api_access_init();
137   api_dvr_init();
138   api_caclient_init();
139   api_profile_init();
140   api_language_init();
141   api_satip_server_init();
142   api_timeshift_init();
143   api_wizard_init();
144 }
145 
api_done(void)146 void api_done ( void )
147 {
148   api_link_t *t;
149 
150   while ((t = RB_FIRST(&api_hook_tree)) != NULL) {
151     RB_REMOVE(&api_hook_tree, t, link);
152     free(t);
153   }
154   SKEL_FREE(api_skel);
155 }
156