1 
2 #ifdef HAVE_CONFIG_H
3 # include <config.h>
4 #endif
5 
6 /* TODO: Consider flushing local icons cache after idling.
7  *       Icon requests will probably come in batches, f.ex. during menu
8  *       browsing.
9  */
10 
11 #include <libgen.h>
12 #include <unistd.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 
16 #include <Eet.h>
17 #include <Ecore.h>
18 #include <Ecore_File.h>
19 #include <Ecore_Ipc.h>
20 #include <Ecore_Con.h>
21 
22 /* define macros and variable for using the eina logging system  */
23 #define EFREET_MODULE_LOG_DOM _efreet_cache_log_dom
24 static int _efreet_cache_log_dom = -1;
25 
26 #include "Efreet.h"
27 #include "efreet_private.h"
28 #include "efreet_cache_private.h"
29 #include "../../static_libs/buildsystem/buildsystem.h"
30 #define NON_EXISTING (void *)-1
31 
32 typedef struct _Efreet_Old_Cache Efreet_Old_Cache;
33 
34 struct _Efreet_Old_Cache
35 {
36     Eina_Hash *hash;
37     Eet_File *ef;
38 };
39 
40 static Ecore_Ipc_Server    *ipc = NULL;
41 static Ecore_Event_Handler *hnd_add = NULL;
42 static Ecore_Event_Handler *hnd_del = NULL;
43 static Ecore_Event_Handler *hnd_data = NULL;
44 
45 static Eina_Lock _lock;
46 
47 /**
48  * Data for cache files
49  */
50 static Eet_Data_Descriptor *directory_edd = NULL;
51 static Eet_Data_Descriptor *icon_theme_edd = NULL;
52 static Eet_Data_Descriptor *icon_theme_directory_edd = NULL;
53 
54 static Eet_Data_Descriptor *icon_fallback_edd = NULL;
55 static Eet_Data_Descriptor *icon_element_pointer_edd = NULL;
56 static Eet_Data_Descriptor *icon_element_edd = NULL;
57 static Eet_Data_Descriptor *icon_edd = NULL;
58 
59 static Eet_File            *icon_cache = NULL;
60 static Eet_File            *fallback_cache = NULL;
61 static Eet_File            *icon_theme_cache = NULL;
62 
63 static Eina_Hash           *themes = NULL;
64 static Eina_Hash           *icons = NULL;
65 static Eina_Hash           *fallbacks = NULL;
66 
67 static const char          *icon_theme_cache_file = NULL;
68 
69 static const char          *theme_name = NULL;
70 
71 static Eet_Data_Descriptor *version_edd = NULL;
72 static Eet_Data_Descriptor *desktop_edd = NULL;
73 static Eet_Data_Descriptor *desktop_action_edd = NULL;
74 static Eet_Data_Descriptor *hash_array_string_edd = NULL;
75 static Eet_Data_Descriptor *array_string_edd = NULL;
76 static Eet_Data_Descriptor *hash_string_edd = NULL;
77 
78 static Eina_Hash           *desktops = NULL;
79 static Eet_File            *desktop_cache = NULL;
80 static const char          *desktop_cache_file = NULL;
81 
82 static Eina_List           *old_desktop_caches = NULL;
83 
84 static const char                *util_cache_file = NULL;
85 static Eet_File                  *util_cache = NULL;
86 static Efreet_Cache_Hash         *util_cache_hash = NULL;
87 static const char                *util_cache_hash_key = NULL;
88 static Efreet_Cache_Array_String *util_cache_names = NULL;
89 static const char                *util_cache_names_key = NULL;
90 
91 static void efreet_cache_edd_shutdown(void);
92 static void efreet_cache_icon_free(Efreet_Cache_Icon *icon);
93 static void efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon);
94 static void efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme);
95 
96 static Eina_Bool efreet_cache_check(Eet_File **ef, const char *path, int major);
97 static void *efreet_cache_close(Eet_File *ef);
98 
99 static void icon_cache_update_free(void *data, void *ev);
100 
101 static void *hash_array_string_add(void *hash, const char *key, void *data);
102 
103 static Eina_Bool disable_cache;
104 static Eina_Bool run_in_tree;
105 static int relaunch_try = 0;
106 
107 EAPI int EFREET_EVENT_ICON_CACHE_UPDATE = 0;
108 EAPI int EFREET_EVENT_DESKTOP_CACHE_UPDATE = 0;
109 EAPI int EFREET_EVENT_DESKTOP_CACHE_BUILD = 0;
110 
111 #define IPC_HEAD(_type) \
112    Ecore_Ipc_Event_Server_##_type *e = event; \
113    if (e->server != ipc) \
114      return ECORE_CALLBACK_PASS_ON
115 
116 static void
_ipc_launch(void)117 _ipc_launch(void)
118 {
119    char buf[PATH_MAX];
120    int num;
121    int try_gap = 10000; // 10ms
122    int tries = 1000; // 1000 * 10ms == 10sec
123    const char *s;
124 
125    if (relaunch_try == 0)
126      {
127         ipc = ecore_ipc_server_connect(ECORE_IPC_LOCAL_USER, "efreetd", 0, NULL);
128         if (ipc)
129           {
130              relaunch_try++;
131              return;
132           }
133      }
134    relaunch_try--;
135    s = getenv("EFREETD_CONNECT_TRIES");
136    if (s)
137      {
138         num = atoi(s);
139         if (num >= 0) tries = num;
140      }
141    s = getenv("EFREETD_CONNECT_TRY_GAP");
142    if (s)
143      {
144         num = atoi(s);
145         if (num >= 0) try_gap = num;
146      }
147    if (run_in_tree)
148      bs_binary_get(buf, sizeof(buf), "efreet", "efreetd");
149    else
150      snprintf(buf, sizeof(buf), PACKAGE_BIN_DIR "/efreetd");
151    ecore_exe_run(buf, NULL);
152    num = 0;
153    while ((!ipc) && (num < tries))
154      {
155         num++;
156         usleep(try_gap);
157         ipc = ecore_ipc_server_connect(ECORE_IPC_LOCAL_USER, "efreetd", 0, NULL);
158      }
159    if (!ipc) ERR("Timeout in trying to start and then connect to efreetd");
160 }
161 
162 static Eina_Bool
_cb_server_add(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)163 _cb_server_add(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
164 {
165    IPC_HEAD(Add);
166    relaunch_try--;
167    return ECORE_CALLBACK_DONE;
168 }
169 
170 static Ecore_Timer *reconnect_timer = NULL;
171 static unsigned int reconnect_count = 0;
172 
173 static Eina_Bool
_cb_server_reconnect(void * data EINA_UNUSED)174 _cb_server_reconnect(void *data EINA_UNUSED)
175 {
176    if (reconnect_timer) ecore_timer_del(reconnect_timer);
177    reconnect_timer = NULL;
178    reconnect_count++;
179    _ipc_launch();
180    if (ipc)
181      {
182         const char *s;
183         int len = 0;
184 
185         s = efreet_language_get();
186         if (s) len = strlen(s);
187         ecore_ipc_server_send(ipc, 1, 0, 0, 0, 0, s, len);
188         efreet_icon_extensions_refresh();
189      }
190    return EINA_FALSE;
191 }
192 
193 static Eina_Bool
_cb_server_del(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)194 _cb_server_del(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
195 {
196    double t;
197    IPC_HEAD(Del);
198    ipc = NULL;
199 
200    if (disable_cache) return ECORE_CALLBACK_RENEW;
201    if (reconnect_count > 10)
202      {
203         char *address = ecore_con_local_path_new(EINA_FALSE, "efreetd", 0);
204         reconnect_timer = NULL;
205         ERR("efreetd connection failed 10 times! check for stale socket file at %s", address);
206         free(address);
207         return EINA_FALSE;
208      }
209    // random 0.5 -> 1.0 sec from now
210    t = (((double)((rand() + (int)getpid()) & 0xff) / 255.0) * 0.5) + 0.5;
211    if (reconnect_timer) ecore_timer_del(reconnect_timer);
212    reconnect_timer = ecore_timer_add(t, _cb_server_reconnect, NULL);
213    return ECORE_CALLBACK_DONE;
214 }
215 
216 static void
_efreet_cache_reset()217 _efreet_cache_reset()
218 {
219    const char *s;
220    int len = 0;
221 
222    if (ipc) ecore_ipc_server_del(ipc);
223    ipc = NULL;
224    if (!disable_cache)
225      ipc = ecore_ipc_server_connect(ECORE_IPC_LOCAL_USER, "efreetd", 0, NULL);
226    if (!ipc) return;
227 
228    s = efreet_language_get();
229    if (s) len = strlen(s);
230    ecore_ipc_server_send(ipc, 1, 0, 0, 0, 0, s, len);
231    efreet_icon_extensions_refresh();
232 }
233 
234 static void
_icon_desktop_cache_update_event_add(int event_type)235 _icon_desktop_cache_update_event_add(int event_type)
236 {
237    Efreet_Event_Cache_Update *ev;
238    Efreet_Old_Cache *d = NULL;
239    Eina_List *l = NULL;
240 
241    efreet_cache_desktop_close();
242 
243    ev = NEW(Efreet_Event_Cache_Update, 1);
244    if (!ev) return;
245 
246    IF_RELEASE(theme_name);
247 
248    // Save all old caches
249    d = NEW(Efreet_Old_Cache, 1);
250    if (d)
251      {
252         d->hash = themes;
253         d->ef = icon_theme_cache;
254         l = eina_list_append(l, d);
255      }
256 
257    d = NEW(Efreet_Old_Cache, 1);
258    if (d)
259      {
260         d->hash = icons;
261         d->ef = icon_cache;
262         l = eina_list_append(l, d);
263      }
264 
265    d = NEW(Efreet_Old_Cache, 1);
266    if (d)
267      {
268         d->hash = fallbacks;
269         d->ef = fallback_cache;
270         l = eina_list_append(l, d);
271      }
272 
273    // Create new empty caches
274    themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free));
275    icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
276    fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free));
277 
278    icon_theme_cache = NULL;
279    icon_cache = NULL;
280    fallback_cache = NULL;
281 
282    // Send event
283    ecore_event_add(event_type, ev, icon_cache_update_free, l);
284 }
285 
286 EAPI void (*_efreet_mime_update_func) (void) = NULL;
287 
288 static Eina_Bool
_cb_server_data(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)289 _cb_server_data(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
290 {
291    IPC_HEAD(Data);
292    if (e->major == 1) // registration
293      {
294         if (e->minor == 1)
295           ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_BUILD, NULL, NULL, NULL);
296      }
297    else if (e->major == 2) // icon cache update
298      {
299         if (e->minor == 1)
300           _icon_desktop_cache_update_event_add(EFREET_EVENT_ICON_CACHE_UPDATE);
301         else
302           ecore_event_add(EFREET_EVENT_ICON_CACHE_UPDATE, NULL, NULL, NULL);
303      }
304    else if (e->major == 3) // desktop cache update
305      {
306         _icon_desktop_cache_update_event_add(EFREET_EVENT_DESKTOP_CACHE_UPDATE);
307      }
308    else if (e->major == 4) // mime cache update
309      {
310         if (_efreet_mime_update_func) _efreet_mime_update_func();
311      }
312    return ECORE_CALLBACK_DONE;
313 }
314 
315 int
efreet_cache_init(void)316 efreet_cache_init(void)
317 {
318     char buf[PATH_MAX];
319 
320     _efreet_cache_log_dom = eina_log_domain_register("efreet_cache", EFREET_DEFAULT_LOG_COLOR);
321     if (_efreet_cache_log_dom < 0)
322         return 0;
323 
324     if (!eina_lock_new(&_lock))
325     {
326         ERR("Could not create lock");
327         goto error;
328     }
329 
330     snprintf(buf, sizeof(buf), "%s/efreet", efreet_cache_home_get());
331     if (!ecore_file_mkpath(buf))
332     {
333         ERR("Failed to create directory '%s'", buf);
334     }
335 
336     run_in_tree = !!getenv("EFL_RUN_IN_TREE");
337 
338     EFREET_EVENT_ICON_CACHE_UPDATE = ecore_event_type_new();
339     EFREET_EVENT_DESKTOP_CACHE_UPDATE = ecore_event_type_new();
340     EFREET_EVENT_DESKTOP_CACHE_BUILD = ecore_event_type_new();
341 
342     themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free));
343     icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
344     fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free));
345     desktops = eina_hash_string_superfast_new(NULL);
346 
347     ecore_ipc_init();
348    // XXX: connect to efreetd
349 
350     if (efreet_cache_update)
351     {
352        if (disable_cache)
353          ipc = NULL;
354        else
355          {
356             ipc = ecore_ipc_server_connect(ECORE_IPC_LOCAL_USER, "efreetd", 0, NULL);
357             if (!ipc) _ipc_launch();
358          }
359        if (ipc)
360          {
361             const char *s;
362             int len = 0;
363 
364             hnd_add = ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_ADD,
365                                               _cb_server_add, NULL);
366             hnd_del = ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DEL,
367                                               _cb_server_del, NULL);
368             hnd_data = ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DATA,
369                                                _cb_server_data, NULL);
370             s = efreet_language_get();
371             if (s) len = strlen(s);
372             ecore_ipc_server_send(ipc, 1, 0, 0, 0, 0, s, len);
373          }
374        else
375          {
376             Efreet_Event_Cache_Update *ev;
377 
378             if (!disable_cache)
379               WRN("Can't contact efreetd daemon for desktop/icon etc. changes");
380             ev = NEW(Efreet_Event_Cache_Update, 1);
381             if (ev)
382               {
383                  ev->error = 1;
384                  ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_BUILD, ev, NULL, NULL);
385               }
386          }
387     }
388     ecore_fork_reset_callback_add(_efreet_cache_reset, NULL);
389 
390     return 1;
391 error:
392     if (themes) eina_hash_free(themes);
393     themes = NULL;
394     if (icons) eina_hash_free(icons);
395     icons = NULL;
396     if (fallbacks) eina_hash_free(fallbacks);
397     fallbacks = NULL;
398     if (desktops) eina_hash_free(desktops);
399     desktops = NULL;
400 
401     efreet_cache_edd_shutdown();
402     return 0;
403 }
404 
405 void
efreet_cache_shutdown(void)406 efreet_cache_shutdown(void)
407 {
408     Efreet_Old_Cache *d;
409 
410     ecore_event_type_flush(EFREET_EVENT_ICON_CACHE_UPDATE,
411                            EFREET_EVENT_DESKTOP_CACHE_UPDATE,
412                            EFREET_EVENT_DESKTOP_CACHE_BUILD);
413     ecore_fork_reset_callback_del(_efreet_cache_reset, NULL);
414     IF_RELEASE(theme_name);
415 
416     icon_cache = efreet_cache_close(icon_cache);
417     icon_theme_cache = efreet_cache_close(icon_theme_cache);
418     fallback_cache = efreet_cache_close(fallback_cache);
419 
420     IF_FREE_HASH(themes);
421     IF_FREE_HASH(icons);
422     IF_FREE_HASH(fallbacks);
423 
424     IF_FREE_HASH_CB(desktops, EINA_FREE_CB(efreet_cache_desktop_free));
425     desktop_cache = efreet_cache_close(desktop_cache);
426     IF_RELEASE(desktop_cache_file);
427 
428     efreet_cache_edd_shutdown();
429     IF_RELEASE(icon_theme_cache_file);
430 
431     if (old_desktop_caches)
432         ERR("This application has not properly closed all its desktop references!");
433     EINA_LIST_FREE(old_desktop_caches, d)
434     {
435         eina_hash_free(d->hash);
436         eet_close(d->ef);
437         free(d);
438     }
439 
440     IF_RELEASE(util_cache_names_key);
441     efreet_cache_array_string_free(util_cache_names);
442     util_cache_names = NULL;
443 
444     IF_RELEASE(util_cache_hash_key);
445     if (util_cache_hash)
446     {
447         eina_hash_free(util_cache_hash->hash);
448         free(util_cache_hash);
449         util_cache_hash = NULL;
450     }
451 
452     util_cache = efreet_cache_close(util_cache);
453     IF_RELEASE(util_cache_file);
454 
455    if (ipc) ecore_ipc_server_del(ipc);
456    if (hnd_add) ecore_event_handler_del(hnd_add);
457    if (hnd_del) ecore_event_handler_del(hnd_del);
458    if (hnd_data) ecore_event_handler_del(hnd_data);
459 
460    ecore_ipc_shutdown();
461 
462    ipc = NULL;
463    hnd_add = NULL;
464    hnd_del = NULL;
465    hnd_data = NULL;
466 
467     eina_lock_free(&_lock);
468 
469     eina_log_domain_unregister(_efreet_cache_log_dom);
470     _efreet_cache_log_dom = -1;
471 }
472 
473 /*
474  * Needs EAPI because of helper binaries
475  */
476 EAPI const char *
efreet_icon_cache_file(const char * theme)477 efreet_icon_cache_file(const char *theme)
478 {
479     static char cache_file[PATH_MAX] = { '\0' };
480     const char *cache;
481 
482     EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
483 
484     cache = efreet_cache_home_get();
485 
486     snprintf(cache_file, sizeof(cache_file), "%s/efreet/icons_%s_%s.eet", cache, theme, efreet_hostname_get());
487 
488     return cache_file;
489 }
490 
491 /*
492  * Needs EAPI because of helper binaries
493  */
494 EAPI const char *
efreet_icon_theme_cache_file(void)495 efreet_icon_theme_cache_file(void)
496 {
497     char tmp[PATH_MAX] = { '\0' };
498 
499     if (icon_theme_cache_file) return icon_theme_cache_file;
500 
501     snprintf(tmp, sizeof(tmp), "%s/efreet/icon_themes_%s.eet",
502              efreet_cache_home_get(), efreet_hostname_get());
503     icon_theme_cache_file = eina_stringshare_add(tmp);
504 
505     return icon_theme_cache_file;
506 }
507 
508 /*
509  * Needs EAPI because of helper binaries
510  */
511 EAPI const char *
efreet_desktop_util_cache_file(void)512 efreet_desktop_util_cache_file(void)
513 {
514     char tmp[PATH_MAX] = { '\0' };
515     const char *cache_dir, *lang, *country, *modifier;
516 
517     if (util_cache_file) return util_cache_file;
518 
519     cache_dir = efreet_cache_home_get();
520     lang = efreet_lang_get();
521     country = efreet_lang_country_get();
522     modifier = efreet_lang_modifier_get();
523 
524     if (lang && country && modifier)
525         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s@%s.eet", cache_dir, efreet_hostname_get(), lang, country, modifier);
526     else if (lang && country)
527         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s.eet", cache_dir, efreet_hostname_get(), lang, country);
528     else if (lang)
529         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s.eet", cache_dir, efreet_hostname_get(), lang);
530     else
531         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s.eet", cache_dir, efreet_hostname_get());
532 
533     util_cache_file = eina_stringshare_add(tmp);
534     return util_cache_file;
535 }
536 
537 /*
538  * Needs EAPI because of helper binaries
539  */
540 #define SHSH(n, v) ((((v) << (n)) & 0xffffffff) | ((v) >> (32 - (n))))
541 
542 static inline int
int_to_bigendian(int in)543 int_to_bigendian(int in)
544 {
545    static const unsigned char test[4] = { 0x11, 0x22, 0x33, 0x44 };
546    static const unsigned int *test_i = (const unsigned int *)test;
547    if (test_i[0] == 0x44332211) return eina_swap32(in);
548    return in;
549 }
550 
551 static void
sha1(unsigned char * data,int size,unsigned char * dst)552 sha1(unsigned char *data, int size, unsigned char *dst)
553 {
554    unsigned int digest[5], word[80], wa, wb, wc, wd, we, t;
555    unsigned char buf[64], *d;
556    int idx, left, i;
557    const unsigned int magic[4] =
558      { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
559 
560    idx = 0;
561    digest[0] = 0x67452301; digest[1] = 0xefcdab89; digest[2] = 0x98badcfe;
562    digest[3] = 0x10325476; digest[4] = 0xc3d2e1f0;
563 
564    memset(buf, 0, sizeof(buf));
565    for (left = size, d = data; left > 0; left--, d++)
566      {
567         if ((idx == 0) && (left < 64))
568           {
569              memset(buf, 0, 60);
570              buf[60] = (size >> 24) & 0xff;
571              buf[61] = (size >> 16) & 0xff;
572              buf[62] = (size >> 8) & 0xff;
573              buf[63] = (size) & 0xff;
574           }
575         buf[idx] = *d;
576         idx++;
577         if ((idx == 64) || (left == 1))
578           {
579              if ((left == 1) && (idx < 64)) buf[idx] = 0x80;
580              for (i = 0; i < 16; i++)
581                {
582                   word[i]  = (unsigned int)buf[(i * 4)    ] << 24;
583                   word[i] |= (unsigned int)buf[(i * 4) + 1] << 16;
584                   word[i] |= (unsigned int)buf[(i * 4) + 2] << 8;
585                   word[i] |= (unsigned int)buf[(i * 4) + 3];
586                }
587              for (i = 16; i < 80; i++)
588                word[i] = SHSH(1,
589                               word[i - 3 ] ^ word[i - 8 ] ^
590                               word[i - 14] ^ word[i - 16]);
591              wa = digest[0]; wb = digest[1]; wc = digest[2];
592              wd = digest[3]; we = digest[4];
593              for (i = 0; i < 80; i++)
594                {
595                   if (i < 20)
596                     t = SHSH(5, wa) + ((wb & wc) | ((~wb) & wd)) +
597                         we + word[i] + magic[0];
598                   else if (i < 40)
599                     t = SHSH(5, wa) + (wb ^ wc ^ wd) +
600                         we + word[i] + magic[1];
601                   else if (i < 60)
602                     t = SHSH(5, wa) + ((wb & wc) | (wb & wd) | (wc & wd)) +
603                         we + word[i] + magic[2];
604                   else if (i < 80)
605                     t = SHSH(5, wa) + (wb ^ wc ^ wd) +
606                         we + word[i] + magic[3];
607                   we = wd;
608                   wd = wc;
609                   wc = SHSH(30, wb);
610                   wb = wa;
611                   wa = t;
612                }
613              digest[0] += wa; digest[1] += wb; digest[2] += wc;
614              digest[3] += wd; digest[4] += we;
615              idx = 0;
616           }
617      }
618    t = int_to_bigendian(digest[0]); digest[0] = t;
619    t = int_to_bigendian(digest[1]); digest[1] = t;
620    t = int_to_bigendian(digest[2]); digest[2] = t;
621    t = int_to_bigendian(digest[3]); digest[3] = t;
622    t = int_to_bigendian(digest[4]); digest[4] = t;
623    memcpy(dst, digest, 5 * 4);
624 }
625 
626 EAPI Eina_Bool
efreet_file_cache_fill(const char * file,Efreet_Cache_Check * check)627 efreet_file_cache_fill(const char *file, Efreet_Cache_Check *check)
628 {
629    struct stat st;
630 
631    memset(check, 0, sizeof(Efreet_Cache_Check));
632 #ifdef _WIN32
633    if (stat(file, &st) != 0) return EINA_FALSE;
634 #else
635    ssize_t size = 0;
636 
637    if (lstat(file, &st) != 0) return EINA_FALSE;
638    if (S_ISLNK(st.st_mode))
639      {
640         char link[PATH_MAX];
641 
642         size = readlink(file, link, sizeof(link));
643         if ((size > 0) && ((size_t)size >= sizeof(link))) return EINA_FALSE;
644         if (stat(file, &st) != 0) return EINA_FALSE;
645      }
646    if (size > 0) sha1((unsigned char *)link, size, check->link_sha1);
647 #endif
648    check->uid    = st.st_uid;
649    check->gid    = st.st_gid;
650    check->size   = st.st_size;
651 #ifndef _WIN32
652    check->blocks = st.st_blocks;
653 #else
654    check->blocks = 0;
655 #endif
656    check->mtime  = st.st_mtime;
657    check->chtime = st.st_ctime;
658    check->mode   = st.st_mode;
659    return EINA_TRUE;
660 }
661 
662 EAPI Eina_Bool // true if matches
efreet_file_cache_check(const Efreet_Cache_Check * check1,const Efreet_Cache_Check * check2)663 efreet_file_cache_check(const Efreet_Cache_Check *check1, const Efreet_Cache_Check *check2)
664 {
665    if ((check1->mtime  != check2->mtime ) ||
666        (check1->size   != check2->size  ) ||
667        (check1->chtime != check2->chtime ) ||
668        (check1->blocks != check2->blocks) ||
669        (check1->mode   != check2->mode  ) ||
670        (check1->uid    != check2->uid   ) ||
671        (check1->gid    != check2->gid   ) ||
672        (memcmp(check1->link_sha1, check2->link_sha1, 20) != 0))
673      {
674         return EINA_FALSE;
675      }
676    return EINA_TRUE; // matches
677 }
678 
679 EAPI Eet_Data_Descriptor *
efreet_version_edd(void)680 efreet_version_edd(void)
681 {
682     Eet_Data_Descriptor_Class eddc;
683 
684     if (version_edd) return version_edd;
685 
686     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Version);
687     version_edd = eet_data_descriptor_file_new(&eddc);
688     if (!version_edd) return NULL;
689 
690     EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version,
691                                   "minor", minor, EET_T_UCHAR);
692     EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version,
693                                   "major", major, EET_T_UCHAR);
694 
695     return version_edd;
696 }
697 
698 /*
699  * Needs EAPI because of helper binaries
700  */
701 EAPI Eet_Data_Descriptor *
efreet_hash_array_string_edd(void)702 efreet_hash_array_string_edd(void)
703 {
704     Eet_Data_Descriptor_Class eddc;
705 
706     if (hash_array_string_edd) return hash_array_string_edd;
707 
708     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash);
709     eddc.func.hash_add = hash_array_string_add;
710     hash_array_string_edd = eet_data_descriptor_file_new(&eddc);
711     if (!hash_array_string_edd) return NULL;
712 
713     EET_DATA_DESCRIPTOR_ADD_HASH(hash_array_string_edd, Efreet_Cache_Hash,
714                                   "hash", hash, efreet_array_string_edd());
715 
716     return hash_array_string_edd;
717 }
718 
719 /*
720  * Needs EAPI because of helper binaries
721  */
722 EAPI Eet_Data_Descriptor *
efreet_hash_string_edd(void)723 efreet_hash_string_edd(void)
724 {
725     Eet_Data_Descriptor_Class eddc;
726 
727     if (hash_string_edd) return hash_string_edd;
728 
729     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash);
730     hash_string_edd = eet_data_descriptor_file_new(&eddc);
731     if (!hash_string_edd) return NULL;
732 
733     EET_DATA_DESCRIPTOR_ADD_HASH_STRING(hash_string_edd, Efreet_Cache_Hash,
734                                   "hash", hash);
735 
736     return hash_string_edd;
737 }
738 
739 /*
740  * Needs EAPI because of helper binaries
741  */
742 EAPI Eet_Data_Descriptor *
efreet_array_string_edd(void)743 efreet_array_string_edd(void)
744 {
745     Eet_Data_Descriptor_Class eddc;
746 
747     if (array_string_edd) return array_string_edd;
748 
749     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Array_String);
750     array_string_edd = eet_data_descriptor_file_new(&eddc);
751     if (!array_string_edd) return NULL;
752     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(array_string_edd, Efreet_Cache_Array_String,
753                                              "array", array);
754 
755     return array_string_edd;
756 }
757 
758 /*
759  * Needs EAPI because of helper binaries
760  */
761 EAPI const char *
efreet_desktop_cache_file(void)762 efreet_desktop_cache_file(void)
763 {
764     char tmp[PATH_MAX] = { '\0' };
765     const char *cache, *lang, *country, *modifier;
766 
767     if (desktop_cache_file) return desktop_cache_file;
768 
769     cache = efreet_cache_home_get();
770     lang = efreet_lang_get();
771     country = efreet_lang_country_get();
772     modifier = efreet_lang_modifier_get();
773 
774     if (lang && country && modifier)
775         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s@%s.eet", cache, efreet_hostname_get(), lang, country, modifier);
776     else if (lang && country)
777         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s.eet", cache, efreet_hostname_get(), lang, country);
778     else if (lang)
779         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s.eet", cache, efreet_hostname_get(), lang);
780     else
781         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s.eet", cache, efreet_hostname_get());
782 
783     desktop_cache_file = eina_stringshare_add(tmp);
784     return desktop_cache_file;
785 }
786 
787 #define EDD_SHUTDOWN(Edd)                       \
788     if (Edd) eet_data_descriptor_free(Edd);       \
789 Edd = NULL;
790 
791 static void
efreet_cache_edd_shutdown(void)792 efreet_cache_edd_shutdown(void)
793 {
794     EDD_SHUTDOWN(version_edd);
795     EDD_SHUTDOWN(desktop_edd);
796     EDD_SHUTDOWN(desktop_action_edd);
797     EDD_SHUTDOWN(hash_array_string_edd);
798     EDD_SHUTDOWN(array_string_edd);
799     EDD_SHUTDOWN(hash_string_edd);
800     EDD_SHUTDOWN(icon_theme_edd);
801     EDD_SHUTDOWN(icon_theme_directory_edd);
802     EDD_SHUTDOWN(directory_edd);
803     EDD_SHUTDOWN(icon_fallback_edd);
804     EDD_SHUTDOWN(icon_element_pointer_edd);
805     EDD_SHUTDOWN(icon_element_edd);
806     EDD_SHUTDOWN(icon_edd);
807 }
808 
809 #define EFREET_POINTER_TYPE(Edd_Dest, Edd_Source, Type)   \
810 {                                                                     \
811     typedef struct _Efreet_##Type##_Pointer Efreet_##Type##_Pointer;   \
812     struct _Efreet_##Type##_Pointer                                    \
813     {                                                                  \
814         Efreet_##Type *pointer;                                         \
815     };                                                                 \
816     \
817     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_##Type##_Pointer); \
818     Edd_Dest = eet_data_descriptor_file_new(&eddc);                    \
819     EET_DATA_DESCRIPTOR_ADD_SUB(Edd_Dest, Efreet_##Type##_Pointer,     \
820                                 "pointer", pointer, Edd_Source);       \
821 }
822 
823 static Eet_Data_Descriptor *
efreet_icon_directory_edd(void)824 efreet_icon_directory_edd(void)
825 {
826     Eet_Data_Descriptor_Class eddc;
827 
828     if (directory_edd) return directory_edd;
829 
830     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Directory);
831     directory_edd = eet_data_descriptor_file_new(&eddc);
832     if (!directory_edd) return NULL;
833 
834    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
835                                  "check.uid", check.uid, EET_T_LONG_LONG);
836    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
837                                  "check.gid", check.gid, EET_T_LONG_LONG);
838    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
839                                  "check.size", check.size, EET_T_LONG_LONG);
840    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
841                                  "check.blocks", check.blocks, EET_T_LONG_LONG);
842    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
843                                  "check.mtime", check.mtime, EET_T_LONG_LONG);
844    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
845                                  "check.chtime", check.chtime, EET_T_LONG_LONG);
846    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
847                                  "check.mode", check.mode, EET_T_INT);
848    EET_DATA_DESCRIPTOR_ADD_BASIC_ARRAY(directory_edd, Efreet_Cache_Directory,
849                                        "check.link_sha1", check.link_sha1, EET_T_CHAR);
850 
851     return directory_edd;
852 }
853 
854 /*
855  * Needs EAPI because of helper binaries
856  */
857 EAPI Eet_Data_Descriptor *
efreet_icon_edd(void)858 efreet_icon_edd(void)
859 {
860     Eet_Data_Descriptor_Class eddc;
861 
862     if (icon_edd) return icon_edd;
863 
864     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Element);
865     icon_element_edd = eet_data_descriptor_file_new(&eddc);
866     if (!icon_element_edd) return NULL;
867 
868     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
869                                   "type", type, EET_T_USHORT);
870     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
871                                   "normal", normal, EET_T_USHORT);
872     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
873                                   "normal", normal, EET_T_USHORT);
874     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
875                                   "min", min, EET_T_USHORT);
876     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
877                                   "max", max, EET_T_USHORT);
878     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_element_edd, Efreet_Cache_Icon_Element,
879                                              "paths", paths);
880 
881     EFREET_POINTER_TYPE(icon_element_pointer_edd, icon_element_edd, Cache_Icon_Element);
882 
883     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon);
884     icon_edd = eet_data_descriptor_file_new(&eddc);
885     if (!icon_edd) return NULL;
886 
887     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_edd, Efreet_Cache_Icon,
888                                   "theme", theme, EET_T_STRING);
889     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(icon_edd, Efreet_Cache_Icon,
890                                       "icons", icons, icon_element_pointer_edd);
891 
892     return icon_edd;
893 }
894 
895 /*
896  * Needs EAPI because of helper binaries
897  */
898 EAPI Eet_Data_Descriptor *
efreet_icon_theme_edd(Eina_Bool cache)899 efreet_icon_theme_edd(Eina_Bool cache)
900 {
901     Eet_Data_Descriptor_Class eddc;
902 
903     if (icon_theme_edd) return icon_theme_edd;
904 
905     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Icon_Theme_Directory);
906     icon_theme_directory_edd = eet_data_descriptor_file_new(&eddc);
907     if (!icon_theme_directory_edd) return NULL;
908 
909     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
910                                   "name", name, EET_T_STRING);
911     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
912                                   "context", context, EET_T_UCHAR);
913     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
914                                   "type", type, EET_T_UCHAR);
915     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
916                                   "size.normal", size.normal, EET_T_UINT);
917     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
918                                   "size.min", size.min, EET_T_UINT);
919     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
920                                   "size.max", size.max, EET_T_UINT);
921     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
922                                   "size.threshold", size.threshold, EET_T_UINT);
923 
924     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Theme);
925     icon_theme_edd = eet_data_descriptor_file_new(&eddc);
926     if (!icon_theme_edd) return NULL;
927 
928     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
929                                   "name.internal", theme.name.internal, EET_T_STRING);
930     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
931                                   "name.name", theme.name.name, EET_T_STRING);
932     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
933                                   "comment", theme.comment, EET_T_STRING);
934     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
935                                   "example_icon", theme.example_icon, EET_T_STRING);
936 
937     eet_data_descriptor_element_add(icon_theme_edd, "paths", EET_T_STRING, EET_G_LIST,
938                                     offsetof(Efreet_Cache_Icon_Theme, theme.paths), 0, NULL, NULL);
939     eet_data_descriptor_element_add(icon_theme_edd, "inherits", EET_T_STRING, EET_G_LIST,
940                                     offsetof(Efreet_Cache_Icon_Theme, theme.inherits), 0, NULL, NULL);
941     EET_DATA_DESCRIPTOR_ADD_LIST(icon_theme_edd, Efreet_Cache_Icon_Theme,
942                                   "directories", theme.directories, icon_theme_directory_edd);
943 
944     if (cache)
945     {
946         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
947                                       "check.uid", check.uid, EET_T_LONG_LONG);
948         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
949                                       "check.gid", check.gid, EET_T_LONG_LONG);
950         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
951                                       "check.size", check.size, EET_T_LONG_LONG);
952         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
953                                       "check.blocks", check.blocks, EET_T_LONG_LONG);
954         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
955                                       "check.mtime", check.mtime, EET_T_LONG_LONG);
956         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
957                                       "check.chtime", check.chtime, EET_T_LONG_LONG);
958         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
959                                       "check.mode", check.mode, EET_T_INT);
960         EET_DATA_DESCRIPTOR_ADD_BASIC_ARRAY(icon_theme_edd, Efreet_Cache_Icon_Theme,
961                                             "check.link_sha1", check.link_sha1, EET_T_CHAR);
962 
963         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
964                                       "path", path, EET_T_STRING);
965 
966         EET_DATA_DESCRIPTOR_ADD_HASH(icon_theme_edd, Efreet_Cache_Icon_Theme,
967                                      "dirs", dirs, efreet_icon_directory_edd());
968     }
969 
970     return icon_theme_edd;
971 }
972 
973 /*
974  * Needs EAPI because of helper binaries
975  */
976 EAPI Eet_Data_Descriptor *
efreet_icon_fallback_edd(void)977 efreet_icon_fallback_edd(void)
978 {
979     Eet_Data_Descriptor_Class eddc;
980 
981     if (icon_fallback_edd) return icon_fallback_edd;
982 
983     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Fallback_Icon);
984     icon_fallback_edd = eet_data_descriptor_file_new(&eddc);
985     if (!icon_fallback_edd) return NULL;
986 
987     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_fallback_edd,
988                                              Efreet_Cache_Fallback_Icon, "icons", icons);
989 
990     return icon_fallback_edd;
991 }
992 
993 /*
994  * Needs EAPI because of helper binaries
995  */
996 EAPI Eet_Data_Descriptor *
efreet_desktop_edd(void)997 efreet_desktop_edd(void)
998 {
999     Eet_Data_Descriptor_Class eddc;
1000 
1001     if (desktop_edd) return desktop_edd;
1002 
1003     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Desktop_Action);
1004     desktop_action_edd = eet_data_descriptor_file_new(&eddc);
1005     if (!desktop_action_edd) return NULL;
1006 
1007     /* Desktop Spec 1.1 */
1008     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_action_edd, Efreet_Desktop_Action, "key", key, EET_T_STRING);
1009     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_action_edd, Efreet_Desktop_Action, "name", name, EET_T_STRING);
1010     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_action_edd, Efreet_Desktop_Action, "icon", icon, EET_T_STRING);
1011     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_action_edd, Efreet_Desktop_Action, "exec", exec, EET_T_STRING);
1012 
1013     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Desktop);
1014     desktop_edd = eet_data_descriptor_file_new(&eddc);
1015     if (!desktop_edd) return NULL;
1016 
1017     /* Desktop Spec 1.0 */
1018     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "type", desktop.type, EET_T_INT);
1019     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "version", desktop.version, EET_T_STRING);
1020     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "orig_path", desktop.orig_path, EET_T_STRING);
1021     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "load_time", desktop.load_time, EET_T_LONG_LONG);
1022     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "name", desktop.name, EET_T_STRING);
1023     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "generic_name", desktop.generic_name, EET_T_STRING);
1024     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "comment", desktop.comment, EET_T_STRING);
1025     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "icon", desktop.icon, EET_T_STRING);
1026     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "try_exec", desktop.try_exec, EET_T_STRING);
1027     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "exec", desktop.exec, EET_T_STRING);
1028     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "path", desktop.path, EET_T_STRING);
1029     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_wm_class", desktop.startup_wm_class, EET_T_STRING);
1030     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "url", desktop.url, EET_T_STRING);
1031     eet_data_descriptor_element_add(desktop_edd, "only_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.only_show_in), 0, NULL, NULL);
1032     eet_data_descriptor_element_add(desktop_edd, "not_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.not_show_in), 0, NULL, NULL);
1033     eet_data_descriptor_element_add(desktop_edd, "categories", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.categories), 0, NULL, NULL);
1034     eet_data_descriptor_element_add(desktop_edd, "mime_types", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.mime_types), 0, NULL, NULL);
1035     eet_data_descriptor_element_add(desktop_edd, "x", EET_T_STRING, EET_G_HASH, offsetof(Efreet_Cache_Desktop, desktop.x), 0, NULL, NULL);
1036     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "no_display", desktop.no_display, EET_T_UCHAR);
1037     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "hidden", desktop.hidden, EET_T_UCHAR);
1038     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "terminal", desktop.terminal, EET_T_UCHAR);
1039     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_notify", desktop.startup_notify, EET_T_UCHAR);
1040 
1041     /* Desktop Spec 1.1 */
1042     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "dbus_activatable", desktop.dbus_activatable, EET_T_UCHAR);
1043     EET_DATA_DESCRIPTOR_ADD_LIST(desktop_edd, Efreet_Cache_Desktop,
1044                                  "actions", desktop.actions, desktop_action_edd);
1045     eet_data_descriptor_element_add(desktop_edd, "implements", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.implements), 0, NULL, NULL);
1046     eet_data_descriptor_element_add(desktop_edd, "keywords", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.keywords), 0, NULL, NULL);
1047 
1048     return desktop_edd;
1049 }
1050 
1051 Efreet_Cache_Icon *
efreet_cache_icon_find(Efreet_Icon_Theme * theme,const char * icon)1052 efreet_cache_icon_find(Efreet_Icon_Theme *theme, const char *icon)
1053 {
1054     Efreet_Cache_Icon *cache = NULL;
1055 
1056     if (theme_name && strcmp(theme_name, theme->name.internal))
1057     {
1058         /* FIXME: this is bad if people have pointer to this cache, things will go wrong */
1059         INF("theme_name change from '%s' to '%s'", theme_name, theme->name.internal);
1060         IF_RELEASE(theme_name);
1061         icon_cache = efreet_cache_close(icon_cache);
1062         eina_hash_free(icons);
1063         icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
1064     }
1065 
1066     if (!efreet_cache_check(&icon_cache, efreet_icon_cache_file(theme->name.internal), EFREET_ICON_CACHE_MAJOR)) return NULL;
1067     if (!theme_name)
1068         theme_name = eina_stringshare_add(theme->name.internal);
1069 
1070     cache = eina_hash_find(icons, icon);
1071     if (cache == NON_EXISTING) return NULL;
1072     if (cache) return cache;
1073 
1074     cache = eet_data_read(icon_cache, efreet_icon_edd(), icon);
1075     if (cache)
1076         eina_hash_add(icons, icon, cache);
1077     else
1078         eina_hash_add(icons, icon, NON_EXISTING);
1079     return cache;
1080 }
1081 
1082 Efreet_Cache_Fallback_Icon *
efreet_cache_icon_fallback_find(const char * icon)1083 efreet_cache_icon_fallback_find(const char *icon)
1084 {
1085     Efreet_Cache_Fallback_Icon *cache;
1086 
1087     if (!efreet_cache_check(&fallback_cache, efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EFREET_ICON_CACHE_MAJOR)) return NULL;
1088 
1089     cache = eina_hash_find(fallbacks, icon);
1090     if (cache == NON_EXISTING) return NULL;
1091     if (cache) return cache;
1092 
1093     cache = eet_data_read(fallback_cache, efreet_icon_fallback_edd(), icon);
1094     if (cache)
1095         eina_hash_add(fallbacks, icon, cache);
1096     else
1097         eina_hash_add(fallbacks, icon, NON_EXISTING);
1098     return cache;
1099 }
1100 
1101 Efreet_Icon_Theme *
efreet_cache_icon_theme_find(const char * theme)1102 efreet_cache_icon_theme_find(const char *theme)
1103 {
1104     Efreet_Cache_Icon_Theme *cache;
1105 
1106     if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL;
1107 
1108     cache = eina_hash_find(themes, theme);
1109     if (cache == NON_EXISTING) return NULL;
1110     if (cache) return &(cache->theme);
1111 
1112     cache = eet_data_read(icon_theme_cache, efreet_icon_theme_edd(EINA_FALSE), theme);
1113     if (cache)
1114     {
1115         eina_hash_add(themes, theme, cache);
1116         return &(cache->theme);
1117     }
1118     else
1119         eina_hash_add(themes, theme, NON_EXISTING);
1120     return NULL;
1121 }
1122 
1123 static void
efreet_cache_icon_free(Efreet_Cache_Icon * icon)1124 efreet_cache_icon_free(Efreet_Cache_Icon *icon)
1125 {
1126     unsigned int i;
1127 
1128     if (!icon) return;
1129     if (icon == NON_EXISTING) return;
1130 
1131     for (i = 0; i < icon->icons_count; ++i)
1132     {
1133         free(icon->icons[i]->paths);
1134         free(icon->icons[i]);
1135     }
1136 
1137     free(icon->icons);
1138     free(icon);
1139 }
1140 
1141 static void
efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon * icon)1142 efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon)
1143 {
1144     if (!icon) return;
1145     if (icon == NON_EXISTING) return;
1146 
1147     free(icon->icons);
1148     free(icon);
1149 }
1150 
1151 static void
efreet_cache_icon_theme_free(Efreet_Icon_Theme * theme)1152 efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme)
1153 {
1154     void *data;
1155 
1156     if (!theme) return;
1157     if (theme == NON_EXISTING) return;
1158 
1159     eina_list_free(theme->paths);
1160     eina_list_free(theme->inherits);
1161     EINA_LIST_FREE(theme->directories, data)
1162         free(data);
1163 
1164     free(theme);
1165 }
1166 
1167 Eina_List *
efreet_cache_icon_theme_list(void)1168 efreet_cache_icon_theme_list(void)
1169 {
1170     Eina_List *ret = NULL;
1171     char **keys;
1172     int i, num;
1173 
1174     if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL;
1175     keys = eet_list(icon_theme_cache, "*", &num);
1176     if (!keys) return NULL;
1177     for (i = 0; i < num; i++)
1178     {
1179         Efreet_Icon_Theme *theme;
1180         if (!strncmp(keys[i], "__efreet", 8)) continue;
1181 
1182         theme = eina_hash_find(themes, keys[i]);
1183         if (!theme)
1184             theme = efreet_cache_icon_theme_find(keys[i]);
1185         if (theme && theme != NON_EXISTING)
1186             ret = eina_list_append(ret, theme);
1187     }
1188     free(keys);
1189     return ret;
1190 }
1191 
1192 /*
1193  * Needs EAPI because of helper binaries
1194  */
1195 EAPI void
efreet_cache_array_string_free(Efreet_Cache_Array_String * array)1196 efreet_cache_array_string_free(Efreet_Cache_Array_String *array)
1197 {
1198     if (!array) return;
1199     free(array->array);
1200     free(array);
1201 }
1202 
1203 Efreet_Desktop *
efreet_cache_desktop_find(const char * file)1204 efreet_cache_desktop_find(const char *file)
1205 {
1206     Efreet_Cache_Desktop *cache = NULL;
1207 
1208     eina_lock_take(&_lock);
1209     if (!efreet_cache_check(&desktop_cache, efreet_desktop_cache_file(), EFREET_DESKTOP_CACHE_MAJOR)) goto error;
1210 
1211     cache = eina_hash_find(desktops, file);
1212     if (cache == NON_EXISTING) goto error;
1213     if (cache)
1214     {
1215         /* If less than one second since last stat, return desktop */
1216         if ((ecore_time_get() - cache->check_time) < 1)
1217         {
1218             INF("Return without stat %f %f for file '%s'", ecore_time_get(), cache->check_time, file);
1219             eina_lock_release(&_lock);
1220             return &cache->desktop;
1221         }
1222         if (cache->desktop.load_time == ecore_file_mod_time(cache->desktop.orig_path))
1223         {
1224             INF("Return with stat %f %f for file '%s'", ecore_time_get(), cache->check_time, file);
1225             cache->check_time = ecore_time_get();
1226             eina_lock_release(&_lock);
1227             return &cache->desktop;
1228         }
1229 
1230         /* We got stale data. The desktop will be free'd eventually as
1231          * users will call efreet_desktop_free */
1232         eina_hash_set(desktops, file, NON_EXISTING);
1233         cache = NULL;
1234     }
1235 
1236     cache = eet_data_read(desktop_cache, efreet_desktop_edd(), file);
1237     if (cache)
1238     {
1239         if (cache->desktop.load_time != ecore_file_mod_time(cache->desktop.orig_path))
1240         {
1241             /* Don't return stale data */
1242             INF("We got stale data in the desktop cache for file '%s'", cache->desktop.orig_path);
1243             efreet_cache_desktop_free(&cache->desktop);
1244             eina_hash_set(desktops, file, NON_EXISTING);
1245         }
1246         else
1247         {
1248             cache->desktop.eet = 1;
1249             cache->check_time = ecore_time_get();
1250             eina_hash_set(desktops, cache->desktop.orig_path, cache);
1251             eina_lock_release(&_lock);
1252             return &cache->desktop;
1253         }
1254     }
1255     else
1256         eina_hash_set(desktops, file, NON_EXISTING);
1257 error:
1258     eina_lock_release(&_lock);
1259     return NULL;
1260 }
1261 
1262 void
efreet_cache_desktop_free(Efreet_Desktop * desktop)1263 efreet_cache_desktop_free(Efreet_Desktop *desktop)
1264 {
1265     Efreet_Old_Cache *d;
1266     Efreet_Desktop *curr;
1267     Eina_List *l;
1268 
1269     if (!desktop ||
1270         desktop == NON_EXISTING ||
1271         !desktop->eet) return;
1272 
1273     eina_lock_take(&_lock);
1274     curr = eina_hash_find(desktops, desktop->orig_path);
1275     if (curr == desktop)
1276     {
1277         INF("Found '%s' in current cache, purge", desktop->orig_path);
1278         eina_hash_del_by_key(desktops, desktop->orig_path);
1279     }
1280 
1281     EINA_LIST_FOREACH(old_desktop_caches, l, d)
1282     {
1283         curr = eina_hash_find(d->hash, desktop->orig_path);
1284         if (curr == desktop)
1285         {
1286             INF("Found '%s' in old cache, purge", desktop->orig_path);
1287             eina_hash_del_by_key(d->hash, desktop->orig_path);
1288             if (eina_hash_population(d->hash) == 0)
1289             {
1290                 INF("Cache empty, close file");
1291                 eina_hash_free(d->hash);
1292                 eet_close(d->ef);
1293                 free(d);
1294                 old_desktop_caches = eina_list_remove_list(old_desktop_caches, l);
1295             }
1296             break;
1297         }
1298     }
1299 
1300     /* Desktop Spec 1.0 */
1301     eina_list_free(desktop->only_show_in);
1302     eina_list_free(desktop->not_show_in);
1303     eina_list_free(desktop->categories);
1304     eina_list_free(desktop->mime_types);
1305     IF_FREE_HASH(desktop->x);
1306     /* Desktop Spec 1.1 */
1307     eina_list_free(desktop->actions);
1308     eina_list_free(desktop->implements);
1309     eina_list_free(desktop->keywords);
1310 
1311     free(desktop);
1312     eina_lock_release(&_lock);
1313 }
1314 
1315 void
efreet_cache_desktop_add(Efreet_Desktop * desktop)1316 efreet_cache_desktop_add(Efreet_Desktop *desktop)
1317 {
1318    char *path;
1319 
1320    if ((!efreet_cache_update) || (!ipc)) return;
1321    if (!eina_main_loop_is()) return;
1322     /*
1323      * TODO: Call in thread with:
1324      *   ecore_thread_main_loop_begin();
1325      *  ecore_thread_main_loop_end();
1326     */
1327    path = ecore_file_dir_get(desktop->orig_path);
1328    if (!path) return;
1329    ecore_ipc_server_send(ipc, 2, 0, 0, 0, 0, path, strlen(path));
1330    free(path);
1331 }
1332 
1333 void
efreet_cache_icon_exts_add(Eina_List * exts)1334 efreet_cache_icon_exts_add(Eina_List *exts)
1335 {
1336    Eina_List *l;
1337    const char *s;
1338    Eina_Binbuf *buf;
1339    int num = 0;
1340    unsigned char nil[1] = { 0 };
1341 
1342    if ((!efreet_cache_update) || (!ipc)) return;
1343    buf = eina_binbuf_new();
1344    if (!buf) return;
1345    EINA_LIST_FOREACH(exts, l, s)
1346      {
1347         if (num > 0) eina_binbuf_append_length(buf, nil, 1);
1348         eina_binbuf_append_length(buf, (unsigned char *)s, strlen(s));
1349         num++;
1350      }
1351    ecore_ipc_server_send(ipc, 5 /* add icon exts */, 0, 0, 0, 0,
1352                          eina_binbuf_string_get(buf),
1353                          eina_binbuf_length_get(buf));
1354    eina_binbuf_free(buf);
1355 }
1356 
1357 void
efreet_cache_icon_dirs_add(Eina_List * dirs)1358 efreet_cache_icon_dirs_add(Eina_List *dirs)
1359 {
1360    Eina_List *l;
1361    const char *s;
1362    Eina_Binbuf *buf;
1363    int num = 0;
1364    unsigned char nil[1] = { 0 };
1365 
1366    if ((!efreet_cache_update) || (!ipc)) return;
1367    buf = eina_binbuf_new();
1368    if (!buf) return;
1369    EINA_LIST_FOREACH(dirs, l, s)
1370      {
1371         if (num > 0) eina_binbuf_append_length(buf, nil, 1);
1372         eina_binbuf_append_length(buf, (unsigned char *)s, strlen(s));
1373         num++;
1374      }
1375    ecore_ipc_server_send(ipc, 4 /* add icon dirs */, 0, 0, 0, 0,
1376                          eina_binbuf_string_get(buf),
1377                          eina_binbuf_length_get(buf));
1378    eina_binbuf_free(buf);
1379 }
1380 
1381 void
efreet_cache_desktop_close(void)1382 efreet_cache_desktop_close(void)
1383 {
1384     IF_RELEASE(util_cache_names_key);
1385     IF_RELEASE(util_cache_hash_key);
1386 
1387     eina_lock_take(&_lock);
1388     if ((desktop_cache) && (desktop_cache != NON_EXISTING))
1389     {
1390         Efreet_Old_Cache *d = NEW(Efreet_Old_Cache, 1);
1391         if (d)
1392         {
1393             d->hash = desktops;
1394             d->ef = desktop_cache;
1395             old_desktop_caches = eina_list_append(old_desktop_caches, d);
1396         }
1397 
1398         desktops = eina_hash_string_superfast_new(NULL);
1399     }
1400     desktop_cache = NULL;
1401     IF_RELEASE(desktop_cache_file);
1402     eina_lock_release(&_lock);
1403 
1404     efreet_cache_array_string_free(util_cache_names);
1405     util_cache_names = NULL;
1406 
1407     if (util_cache_hash)
1408     {
1409         eina_hash_free(util_cache_hash->hash);
1410         free(util_cache_hash);
1411         util_cache_hash = NULL;
1412     }
1413 
1414     util_cache = efreet_cache_close(util_cache);
1415     IF_RELEASE(util_cache_file);
1416 }
1417 
1418 void
efreet_cache_desktop_build(void)1419 efreet_cache_desktop_build(void)
1420 {
1421    const char *s;
1422    int len = 0;
1423    if ((!efreet_cache_update) || (!ipc)) return;
1424    s = efreet_language_get();
1425    if (s) len = strlen(s);
1426    ecore_ipc_server_send(ipc, 3 /* build desktop cache */, 0, 0, 0, 0, s, len);
1427 }
1428 
1429 static Eina_Bool
efreet_cache_check(Eet_File ** ef,const char * path,int major)1430 efreet_cache_check(Eet_File **ef, const char *path, int major)
1431 {
1432     Efreet_Cache_Version *version;
1433 
1434     if (*ef == NON_EXISTING) return EINA_FALSE;
1435     if (*ef) return EINA_TRUE;
1436     if (!*ef)
1437         *ef = eet_open(path, EET_FILE_MODE_READ);
1438     if (!*ef)
1439     {
1440         *ef = NON_EXISTING;
1441         return EINA_FALSE;
1442     }
1443 
1444     version = eet_data_read(*ef, efreet_version_edd(), EFREET_CACHE_VERSION);
1445     if ((!version) || (version->major != major))
1446     {
1447         IF_FREE(version);
1448         eet_close(*ef);
1449         *ef = NON_EXISTING;
1450         return EINA_FALSE;
1451     }
1452     free(version);
1453     return EINA_TRUE;
1454 }
1455 
1456 static void *
efreet_cache_close(Eet_File * ef)1457 efreet_cache_close(Eet_File *ef)
1458 {
1459     if (ef && ef != NON_EXISTING)
1460         eet_close(ef);
1461     return NULL;
1462 }
1463 
1464 Efreet_Cache_Hash *
efreet_cache_util_hash_string(const char * key)1465 efreet_cache_util_hash_string(const char *key)
1466 {
1467     if (util_cache_hash_key && !strcmp(key, util_cache_hash_key))
1468         return util_cache_hash;
1469     if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
1470 
1471     if (util_cache_hash)
1472     {
1473         /* free previous util_cache */
1474         IF_RELEASE(util_cache_hash_key);
1475         eina_hash_free(util_cache_hash->hash);
1476         free(util_cache_hash);
1477     }
1478     util_cache_hash_key = eina_stringshare_add(key);
1479     util_cache_hash = eet_data_read(util_cache, efreet_hash_string_edd(), key);
1480     return util_cache_hash;
1481 }
1482 
1483 Efreet_Cache_Hash *
efreet_cache_util_hash_array_string(const char * key)1484 efreet_cache_util_hash_array_string(const char *key)
1485 {
1486     if (util_cache_hash_key && !strcmp(key, util_cache_hash_key))
1487         return util_cache_hash;
1488     if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
1489 
1490     IF_RELEASE(util_cache_hash_key);
1491     if (util_cache_hash)
1492     {
1493         /* free previous cache */
1494         eina_hash_free(util_cache_hash->hash);
1495         free(util_cache_hash);
1496     }
1497     util_cache_hash_key = eina_stringshare_add(key);
1498     util_cache_hash = eet_data_read(util_cache, efreet_hash_array_string_edd(), key);
1499     return util_cache_hash;
1500 }
1501 
1502 Efreet_Cache_Array_String *
efreet_cache_util_names(const char * key)1503 efreet_cache_util_names(const char *key)
1504 {
1505     if (util_cache_names_key && !strcmp(key, util_cache_names_key))
1506         return util_cache_names;
1507     if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
1508 
1509     if (util_cache_names)
1510     {
1511         /* free previous util_cache */
1512         IF_RELEASE(util_cache_names_key);
1513         efreet_cache_array_string_free(util_cache_names);
1514     }
1515     util_cache_names_key = eina_stringshare_add(key);
1516     util_cache_names = eet_data_read(util_cache, efreet_array_string_edd(), key);
1517     return util_cache_names;
1518 }
1519 
1520 static void
icon_cache_update_free(void * data,void * ev)1521 icon_cache_update_free(void *data, void *ev)
1522 {
1523     Efreet_Old_Cache *d;
1524     Eina_List *l;
1525 
1526     l = data;
1527     EINA_LIST_FREE(l, d)
1528     {
1529         if (d->hash)
1530             eina_hash_free(d->hash);
1531         efreet_cache_close(d->ef);
1532         free(d);
1533     }
1534     free(ev);
1535 }
1536 
1537 static void *
hash_array_string_add(void * hash,const char * key,void * data)1538 hash_array_string_add(void *hash, const char *key, void *data)
1539 {
1540     if (!hash)
1541         hash = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
1542     if (!hash)
1543         return NULL;
1544     eina_hash_add(hash, key, data);
1545     return hash;
1546 }
1547 
1548 EAPI void
efreet_cache_disable(void)1549 efreet_cache_disable(void)
1550 {
1551    Eina_Bool prev = disable_cache;
1552 
1553    disable_cache = EINA_TRUE;
1554 
1555    if (_efreet_cache_log_dom < 0) return; // not yet initialized
1556    if (prev == disable_cache) return; // same value
1557    if (ipc)
1558      {
1559         ecore_ipc_server_del(ipc);
1560         ipc = NULL;
1561      }
1562 }
1563 
1564 EAPI void
efreet_cache_enable(void)1565 efreet_cache_enable(void)
1566 {
1567    Eina_Bool prev = disable_cache;
1568 
1569    disable_cache = EINA_FALSE;
1570 
1571    if (_efreet_cache_log_dom < 0) return; // not yet initialized
1572    if (prev == disable_cache) return; // same value
1573    if (!ipc) _ipc_launch();
1574 }
1575