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", ¶m_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