1 #include "e_mod_main.h"
2 
3 static const char E_FILEMAN_BUS_NAME[] = "org.enlightenment.FileManager";
4 static const char E_FILEMAN_INTERFACE[] = "org.enlightenment.FileManager";
5 static const char E_FILEMAN_ERROR[] = "org.enlightenment.FileManager.Error";
6 static const char E_FILEMAN_PATH[] = "/org/enlightenment/FileManager";
7 
8 typedef struct _E_Fileman_DBus_Daemon E_Fileman_DBus_Daemon;
9 struct _E_Fileman_DBus_Daemon
10 {
11    Eldbus_Connection *conn;
12    Eldbus_Service_Interface *iface;
13 };
14 
15 static Eldbus_Message * _e_fileman_dbus_daemon_open_directory_cb(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg);
16 static Eldbus_Message *_e_fileman_dbus_daemon_open_file_cb(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg);
17 
18 static Eldbus_Message *
_e_fileman_dbus_daemon_error(const Eldbus_Message * msg,const char * error_msg)19 _e_fileman_dbus_daemon_error(const Eldbus_Message *msg,
20                              const char  *error_msg)
21 {
22    return eldbus_message_error_new(msg, E_FILEMAN_ERROR, error_msg);
23 }
24 
25 static const Eldbus_Method methods[] = {
26    { "OpenDirectory", ELDBUS_ARGS({"s", "directory"}), NULL,
27       _e_fileman_dbus_daemon_open_directory_cb, 0 },
28    { "OpenFile", ELDBUS_ARGS({"s", "file"}), NULL,
29       _e_fileman_dbus_daemon_open_file_cb, 0 },
30    { NULL, NULL, NULL, NULL, 0 }
31 };
32 
33 static const Eldbus_Service_Interface_Desc desc = {
34    E_FILEMAN_INTERFACE, methods, NULL, NULL, NULL, NULL
35 };
36 
37 static void
_e_fileman_dbus_daemon_object_init(E_Fileman_DBus_Daemon * d)38 _e_fileman_dbus_daemon_object_init(E_Fileman_DBus_Daemon *d)
39 {
40    d->iface = eldbus_service_interface_register(d->conn, E_FILEMAN_PATH, &desc);
41    if (!d->iface)
42      {
43         fprintf(stderr, "ERROR: cannot add object to %s\n", E_FILEMAN_PATH);
44         return;
45      }
46 }
47 
48 static void
_e_fileman_dbus_daemon_free(E_Fileman_DBus_Daemon * d)49 _e_fileman_dbus_daemon_free(E_Fileman_DBus_Daemon *d)
50 {
51    if (d->iface)
52      eldbus_service_object_unregister(d->iface);
53    if (d->conn)
54      eldbus_connection_unref(d->conn);
55 
56    free(d);
57 }
58 
59 static Eina_Bool
_e_fileman_dbus_call_rate_limit(void)60 _e_fileman_dbus_call_rate_limit(void)
61 {
62    static double last_call = 0.0;
63    static unsigned long long last_calls = 0;
64    double t = ecore_time_get();
65 
66    if ((t - last_call) < 0.5) last_calls++;
67    else last_calls = 0;
68    last_call = t;
69    // if we get more than 10 requests over 0.5 sec - rate limit
70    if (last_calls > 10) return EINA_TRUE;
71    return EINA_FALSE;
72 }
73 
74 static Eldbus_Message *
_e_fileman_dbus_daemon_open_directory_cb(const Eldbus_Service_Interface * iface EINA_UNUSED,const Eldbus_Message * msg)75 _e_fileman_dbus_daemon_open_directory_cb(const Eldbus_Service_Interface *iface EINA_UNUSED,
76                                          const Eldbus_Message *msg)
77 {
78    const char *directory = NULL, *p;
79    char *dev, *to_free = NULL;
80    E_Zone *zone;
81 
82 
83    if (_e_fileman_dbus_call_rate_limit())
84      {
85         fprintf(stderr, "EFM remote call rate limiting to avoid DOS attacks");
86         return eldbus_message_method_return_new(msg);
87      }
88    if (!eldbus_message_arguments_get(msg, "s", &directory))
89      {
90         fprintf(stderr, "Error: getting arguments of OpenDirectory call.\n");
91         return eldbus_message_method_return_new(msg);
92      }
93 
94    if ((!directory) || (directory[0] == '\0'))
95      return _e_fileman_dbus_daemon_error(msg, "no directory provided.");
96 
97    zone = e_zone_current_get();
98    if (!zone)
99      return _e_fileman_dbus_daemon_error(msg, "could not find a zone.");
100 
101    if (strstr(directory, "://"))
102      {
103         Efreet_Uri *uri = efreet_uri_decode(directory);
104 
105         directory = NULL;
106         if (uri)
107           {
108              if ((uri->protocol) && (strcmp(uri->protocol, "file") == 0))
109                directory = to_free = strdup(uri->path);
110              efreet_uri_free(uri);
111           }
112 
113         if (!directory)
114           return _e_fileman_dbus_daemon_error(msg, "unsupported protocol");
115      }
116 
117    p = strchr(directory, '/');
118    if (p)
119      {
120         int len = p - directory + 1;
121 
122         dev = malloc(len + 1);
123         if (!dev)
124           {
125              free(to_free);
126              return _e_fileman_dbus_daemon_error(msg,
127                                                  "could not allocate memory.");
128           }
129 
130         memcpy(dev, directory, len);
131         dev[len] = '\0';
132 
133         if ((dev[0] != '/') && (dev[0] != '~'))
134           dev[len - 1] = '\0';  /* remove trailing '/' */
135 
136         directory += p - directory;
137      }
138    else
139      {
140         dev = strdup(directory);
141         directory = "/";
142      }
143 
144    e_fwin_new(dev, directory);
145    free(dev);
146    free(to_free);
147    return eldbus_message_method_return_new(msg);
148 }
149 
150 static Eina_Bool
_mime_shell_script_check(const char * mime)151 _mime_shell_script_check(const char *mime)
152 {
153    static const struct sh_script_map {
154       const char *str;
155       size_t len;
156    } options[] = {
157 #define O(x) {x, sizeof(x) - 1}
158      O("application/x-sh"),
159      O("application/x-shellscript"),
160      O("text/x-sh"),
161 #undef O
162      {NULL, 0}
163    };
164    const struct sh_script_map *itr;
165    size_t mimelen = strlen(mime);
166 
167    for (itr = options; itr->str != NULL; itr++)
168      if ((mimelen == itr->len) && (memcmp(mime, itr->str, mimelen) == 0))
169        return EINA_TRUE;
170 
171    return EINA_FALSE;
172 }
173 
174 static Eldbus_Message*
_e_fileman_dbus_daemon_open_file_cb(const Eldbus_Service_Interface * iface EINA_UNUSED,const Eldbus_Message * msg)175 _e_fileman_dbus_daemon_open_file_cb(const Eldbus_Service_Interface *iface EINA_UNUSED,
176                                     const Eldbus_Message *msg)
177 {
178    Eina_List *handlers;
179    const char *param_file = NULL, *mime, *errmsg = "unknown error";
180    char *real_file, *to_free = NULL;
181    E_Zone *zone;
182 
183    if (_e_fileman_dbus_call_rate_limit())
184      {
185         fprintf(stderr, "EFM remote call rate limiting to avoid DOS attacks");
186         return eldbus_message_method_return_new(msg);
187      }
188    if (!eldbus_message_arguments_get(msg, "s", &param_file))
189      {
190         fprintf(stderr, "ERROR: getting arguments of OpenFile call.\n");
191         return eldbus_message_method_return_new(msg);
192      }
193 
194    if ((!param_file) || (param_file[0] == '\0'))
195      return _e_fileman_dbus_daemon_error(msg, "no file provided.");
196 
197    zone = e_zone_current_get();
198    if (!zone)
199      return _e_fileman_dbus_daemon_error(msg, "could not find a zone.");
200 
201    if (!strstr(param_file, "://"))
202      {
203         real_file = ecore_file_realpath(param_file);
204         if (!real_file)
205           {
206              errmsg = "couldn't get realpath for file.";
207              goto error;
208           }
209      }
210    else
211      {
212         Efreet_Uri *uri = efreet_uri_decode(param_file);
213 
214         real_file = NULL;
215         if (uri)
216           {
217              if ((uri->protocol) && (strcmp(uri->protocol, "file") == 0))
218                {
219                   real_file = ecore_file_realpath(uri->path);
220                   param_file = to_free = strdup(uri->path);
221                }
222              efreet_uri_free(uri);
223           }
224 
225         if (!real_file)
226           {
227              errmsg = "unsupported protocol";
228              goto error;
229           }
230      }
231 
232    mime = efreet_mime_type_get(real_file);
233    if (!mime)
234      {
235         errmsg = "couldn't find mime-type";
236         goto error;
237      }
238 
239    if (strcmp(mime, "application/x-desktop") == 0)
240      {
241         Efreet_Desktop *desktop = efreet_desktop_new(real_file);
242         if (!desktop)
243           {
244              errmsg = "couldn't open desktop file";
245              goto error;
246           }
247 
248         e_exec(zone, desktop, NULL, NULL, NULL);
249         efreet_desktop_free(desktop);
250         goto end;
251      }
252    else if ((strcmp(mime, "application/x-executable") == 0) ||
253             ecore_file_can_exec(param_file))
254      {
255         e_exec(zone, NULL, param_file, NULL, NULL);
256         goto end;
257      }
258    else if (_mime_shell_script_check(mime))
259      {
260         Eina_Strbuf *b = eina_strbuf_new();
261         const char *shell = getenv("SHELL");
262         if (!shell)
263           {
264              uid_t uid = getuid();
265              struct passwd *pw = getpwuid(uid);
266              if (pw) shell = pw->pw_shell;
267           }
268         if (!shell) shell = "/bin/sh";
269         eina_strbuf_append_printf(b, "%s %s %s",
270                                   e_config->exebuf_term_cmd,
271                                   shell,
272                                   param_file);
273         e_exec(zone, NULL, eina_strbuf_string_get(b), NULL, NULL);
274         eina_strbuf_free(b);
275         goto end;
276      }
277 
278    handlers = efreet_util_desktop_mime_list(mime);
279    if (!handlers)
280      {
281         errmsg = "no handlers for given file";
282         goto end;
283      }
284    else
285      {
286         Efreet_Desktop *desktop = handlers->data;
287         Eina_List *files = eina_list_append(NULL, param_file);
288 
289         e_exec(zone, desktop, NULL, files, NULL);
290         eina_list_free(files);
291 
292         EINA_LIST_FREE(handlers, desktop)
293           efreet_desktop_free(desktop);
294      }
295 
296  end:
297    free(real_file);
298    free(to_free);
299    return eldbus_message_method_return_new(msg);
300 
301  error:
302    free(real_file);
303    free(to_free);
304    return _e_fileman_dbus_daemon_error(msg, errmsg);
305 }
306 
307 static E_Fileman_DBus_Daemon *
_e_fileman_dbus_daemon_new(void)308 _e_fileman_dbus_daemon_new(void)
309 {
310    E_Fileman_DBus_Daemon *d;
311 
312    d = calloc(1, sizeof(E_Fileman_DBus_Daemon));
313    if (!d)
314      {
315         perror("ERROR: FILEMAN: cannot allocate fileman dbus daemon memory.");
316         return NULL;
317      }
318 
319    d->conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
320    if (!d->conn)
321      goto error;
322 
323    _e_fileman_dbus_daemon_object_init(d);
324    eldbus_name_request(d->conn, E_FILEMAN_BUS_NAME,
325                       ELDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, NULL, NULL);
326    return d;
327 
328 error:
329    fprintf(stderr, "ERROR: FILEMAN: failed to create daemon at %p\n", d);
330    _e_fileman_dbus_daemon_free(d);
331    return NULL;
332 }
333 
334 static E_Fileman_DBus_Daemon *_daemon = NULL;
335 
336 void
e_fileman_dbus_init(void)337 e_fileman_dbus_init(void)
338 {
339    if (_daemon)
340      return;
341 
342    _daemon = _e_fileman_dbus_daemon_new();
343 }
344 
345 void
e_fileman_dbus_shutdown(void)346 e_fileman_dbus_shutdown(void)
347 {
348    if (!_daemon)
349      return;
350 
351    _e_fileman_dbus_daemon_free(_daemon);
352    _daemon = NULL;
353 }
354 
355