1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail : see the 'copyright' file.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
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 <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23 #include <math.h>
24
25 #include <glib/gstdio.h>
26
27 #include "applet-struct.h"
28 #include "applet-notifications.h"
29 #include "applet-session.h"
30 #include "applet-listing.h"
31 #include "applet-search.h"
32 #include "applet-backend-files.h"
33
34 typedef struct _CDBookmarkItem{
35 gchar *cName;
36 gchar *cLowerCaseName;
37 gchar *cAddress;
38 gchar *cComment;
39 gchar *cIcon64;
40 GList *pSubItems;
41 } CDBookmarkItem;
42
43 // sub-listing
44 static GList *_cd_do_list_bookmarks_actions (CDEntry *pEntry, int *iNbEntries);
45 static GList *_cd_do_list_bookmarks_folder (CDEntry *pEntry, int *iNbEntries);
46 // fill entry
47 static gboolean _cd_do_fill_bookmark_entry (CDEntry *pEntry);
48 // actions
49 static void _cd_do_launch_url (CDEntry *pEntry);
50 static void _cd_do_copy_url (CDEntry *pEntry);
51
52 static gchar *s_cBookmarksFile = NULL;
53 static CDBookmarkItem *s_pRootItem = NULL;
54
55 //////////
56 // INIT //
57 //////////
58
59 static void _on_file_event (CairoDockFMEventType iEventType, const gchar *cURI, gpointer data);
60
_free_item(CDBookmarkItem * pItem)61 static void _free_item (CDBookmarkItem *pItem)
62 {
63 if (pItem == NULL)
64 return ;
65 g_free (pItem->cName);
66 g_free (pItem->cLowerCaseName);
67 g_free (pItem->cComment);
68 g_free (pItem->cAddress);
69 g_list_foreach (pItem->pSubItems, (GFunc)_free_item, NULL);
70 g_free (pItem);
71 }
72
_get_bookmarks_path(void)73 static gchar *_get_bookmarks_path (void)
74 {
75 gchar *cPath = g_strdup_printf ("%s/.mozilla/firefox", g_getenv ("HOME"));
76 GError *erreur = NULL;
77 GDir *dir = g_dir_open (cPath, 0, &erreur);
78 if (erreur != NULL)
79 {
80 cd_warning (erreur->message);
81 g_error_free (erreur);
82 g_free (cPath);
83 return NULL;
84 }
85
86 gchar *cBookmarks = NULL;
87 const gchar *cFileName;
88 do
89 {
90 cFileName = g_dir_read_name (dir);
91 if (cFileName == NULL)
92 break ;
93 cBookmarks = g_strdup_printf ("%s/%s/bookmarks.html", cPath, cFileName);
94 if (g_file_test (cBookmarks, G_FILE_TEST_EXISTS))
95 break; // on en prend qu'un.
96 else
97 {
98 g_free (cBookmarks);
99 cBookmarks = NULL;
100 }
101 }
102 while (1);
103 g_dir_close (dir);
104
105 g_free (cPath);
106 return cBookmarks;
107 }
108
_parse_folder(gchar * cContent,gchar ** cNewPosition)109 static GList *_parse_folder (gchar *cContent, gchar **cNewPosition)
110 {
111 GList *pList = NULL;
112 CDBookmarkItem *pItem = NULL, *pFolderItem = NULL;
113 gchar *str, *str2, *ptr=cContent;
114
115 do
116 {
117 str = strchr (ptr, '<');
118 if (!str)
119 break;
120 str ++;
121 if (*str == 'H' && *(str+1) == '3') // nouveau repertoire. <H3 ...>nom</H3>
122 {
123 str = strchr (str+2, '>'); // fin de la balise <H3>
124 str ++;
125
126 str2 = strchr (str, '<'); // debut de la balise fermante </H3>
127 if (str2 != str)
128 {
129 pFolderItem = g_new0 (CDBookmarkItem, 1);
130 pFolderItem->cName = g_strndup (str, str2-str);
131 pFolderItem->cLowerCaseName = g_ascii_strdown (pFolderItem->cName, -1);
132 pList = g_list_prepend (pList, pFolderItem);
133 }
134 ptr = str2 + 5;
135 }
136 else if (*str == 'D' && *(str+1) == 'L') // debut de contenu du repertoire. <DL> sub-items </DL>
137 {
138 pFolderItem->pSubItems = _parse_folder (str+4, &ptr); // la fonction nous place apres le </DL> correspondant.
139 pFolderItem = NULL;
140 }
141 else if (*str == 'A') // nouvelle adresse. <A HREF="adresse"> nom </A>
142 {
143 str = g_strstr_len (str+2, -1, "HREF=\""); // debut d'adresse.
144 str += 6;
145 str2 = strchr (str, '"'); // fin de l'adresse.
146 pItem = g_new0 (CDBookmarkItem, 1);
147 pItem->cAddress = g_strndup (str, str2-str);
148 pList = g_list_prepend (pList, pItem);
149
150 str = str2 + 1;
151 str2 = strchr (str, '>'); // fin de la balise <A>
152 gchar *icon = g_strstr_len (str, str2 - str, "ICON=\"data:"); // ICON="data:image/x-icon;base64,ABCEDF..."
153 if (icon)
154 {
155 icon += 11;
156 if (*icon != '"') // sinon aucune donnee.
157 {
158 icon = strchr (icon+1, ',');
159 if (icon)
160 {
161 icon ++;
162 str = strchr (icon, '"');
163 pItem->cIcon64 = g_strndup (icon, str-icon);
164 }
165 }
166 }
167
168 str = str2 + 1;
169 str2 = strchr (str, '<'); // debut de la balise fermante </A>
170 pItem->cName = g_strndup (str, str2-str);
171 pItem->cLowerCaseName = g_ascii_strdown (pItem->cName, -1);
172
173 ptr = str2 + 4;
174 }
175 else if (*str == '/' && *(str+1) == 'D' && *(str+2) == 'L') // fin du repertoire. <DL> sub-items </DL>
176 {
177 ptr = str + 4;
178 break;
179 }
180 else if (*str == 'D' && *(str+1) == 'D') // balise de commentaire. <DD> commentaire
181 {
182 str += 4;
183 str2 = strchr (str, '<'); // debut d'une autre balise.
184 if (pFolderItem != NULL)
185 pFolderItem->cComment = g_strndup (str, str2-str);
186 else if (pItem != NULL)
187 pItem->cComment = g_strndup (str, str2-str);
188 ptr = str2;
189 }
190 else // balise ininteressante, on la saute.
191 {
192 str2 = strchr (str, '>'); // fin de la balise.
193 ptr = str2 + 1;
194 }
195 } while (1);
196
197 *cNewPosition = ptr;
198 return pList;
199 }
200
_parse_bookmarks(const gchar * cFilePath)201 static CDBookmarkItem *_parse_bookmarks (const gchar *cFilePath)
202 {
203 gsize length = 0;
204 gchar *cBookmarksContent = NULL;
205 g_file_get_contents (cFilePath,
206 &cBookmarksContent,
207 &length,
208 NULL);
209 if (cBookmarksContent == NULL)
210 {
211 cd_warning ("can't read bookmarks");
212 return NULL;
213 }
214
215 gchar *str = g_strstr_len (cBookmarksContent, -1, "<DL>");
216 if (!str)
217 {
218 cd_warning ("empty bookmarks");
219 return NULL;
220 }
221 CDBookmarkItem *pRootItem = g_new0 (CDBookmarkItem, 1);
222 pRootItem->pSubItems = _parse_folder (str+4, &str);
223
224 g_free (cBookmarksContent);
225 return pRootItem;
226 }
227
init(void)228 static gboolean init (void)
229 {
230 // on trouve le fichier des bookmarks.
231 s_cBookmarksFile = _get_bookmarks_path ();
232 if (s_cBookmarksFile == NULL)
233 {
234 cd_warning ("no bookmarks");
235 return FALSE;
236 }
237 cd_debug ("found bookmarks '%s'", s_cBookmarksFile);
238
239 // on parse le fichier.
240 s_pRootItem = _parse_bookmarks (s_cBookmarksFile);
241
242 // on surveille le fichier.
243 cairo_dock_fm_add_monitor_full (s_cBookmarksFile, FALSE, NULL, (CairoDockFMMonitorCallback) _on_file_event, NULL);
244
245 return TRUE;
246 }
247
stop(void)248 static void stop (void)
249 {
250 if (s_cBookmarksFile == NULL)
251 return ;
252
253 cairo_dock_fm_remove_monitor_full (s_cBookmarksFile, FALSE, NULL);
254 g_free (s_cBookmarksFile);
255 s_cBookmarksFile = NULL;
256 _free_item (s_pRootItem);
257 s_pRootItem = NULL;
258 }
259
_on_file_event(CairoDockFMEventType iEventType,const gchar * cURI,gpointer data)260 static void _on_file_event (CairoDockFMEventType iEventType, const gchar *cURI, gpointer data)
261 {
262 cd_debug ("bookmarks have changed\n");
263 switch (iEventType)
264 {
265 case CAIRO_DOCK_FILE_DELETED :
266 case CAIRO_DOCK_FILE_CREATED :
267 case CAIRO_DOCK_FILE_MODIFIED :
268 stop ();
269 init ();
270 break;
271
272 default :
273 break;
274 }
275 }
276
277 ////////////////
278 // FILL ENTRY //
279 ////////////////
280
_cd_do_fill_bookmark_entry(CDEntry * pEntry)281 static gboolean _cd_do_fill_bookmark_entry (CDEntry *pEntry)
282 {
283 if (pEntry->pIconSurface != NULL || pEntry->cIconName == NULL)
284 return FALSE;
285
286 gsize out_len = 0;
287 //g_print ("icon : %s\n", pEntry->cIconName);
288 guchar *icon = g_base64_decode (pEntry->cIconName, &out_len);
289 //g_print ("-> out_len : %d\n", out_len);
290 g_return_val_if_fail (icon != NULL, FALSE);
291 //g_print ("-> data : %d\n", icon);
292
293 GInputStream * is = g_memory_input_stream_new_from_data (icon,
294 out_len,
295 NULL);
296 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream (is,
297 NULL,
298 NULL);
299 g_object_unref (is);
300 double fImageWidth=0, fImageHeight=0;
301 double fZoomX=0, fZoomY=0;
302 pEntry->pIconSurface = cairo_dock_create_surface_from_pixbuf (pixbuf,
303 1.,
304 myDialogsParam.dialogTextDescription.iSize, myDialogsParam.dialogTextDescription.iSize,
305 0,
306 &fImageWidth, &fImageHeight,
307 &fZoomX, &fZoomY);
308 g_object_unref (pixbuf);
309 g_free (icon);
310
311 return TRUE;
312 }
313
314
315 /////////////
316 // ACTIONS //
317 /////////////
318
_cd_do_launch_url(CDEntry * pEntry)319 static void _cd_do_launch_url (CDEntry *pEntry)
320 {
321 cd_debug ("%s (%s)", __func__, pEntry->cPath);
322 //cairo_dock_fm_launch_uri (pEntry->cPath);
323 cairo_dock_launch_command_printf ("firefox \"%s\"", NULL, pEntry->cPath);
324 }
325
_cd_do_launch_in_new_window(CDEntry * pEntry)326 static void _cd_do_launch_in_new_window (CDEntry *pEntry)
327 {
328 cd_debug ("%s (%s)", __func__, pEntry->cPath);
329 cairo_dock_launch_command_printf ("firefox -no-remote \"%s\"", NULL, pEntry->cPath);
330 }
331
_cd_do_copy_url(CDEntry * pEntry)332 static void _cd_do_copy_url (CDEntry *pEntry)
333 {
334 cd_debug ("%s (%s)", __func__, pEntry->cPath);
335 GtkClipboard *pClipBoard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
336 gtk_clipboard_set_text (pClipBoard, pEntry->cPath, -1);
337 }
338
_cd_do_launch_all_url(CDEntry * pEntry)339 static void _cd_do_launch_all_url (CDEntry *pEntry)
340 {
341 cd_debug ("%s (%s)", __func__, pEntry->cPath);
342 cairo_dock_launch_command_printf ("firefox %s", NULL, pEntry->cPath);
343 }
344
345 /////////////////
346 // SUB-LISTING //
347 /////////////////
348 #define NB_ACTIONS_ON_BOOKMARKS 3
349
_cd_do_list_bookmarks_actions(CDEntry * pEntry,int * iNbEntries)350 static GList *_cd_do_list_bookmarks_actions (CDEntry *pEntry, int *iNbEntries)
351 {
352 GList *pEntries = NULL;
353 CDEntry *pSubEntry;
354
355 pSubEntry = g_new0 (CDEntry, 1);
356 pSubEntry->cPath = g_strdup (pEntry->cPath);
357 pSubEntry->cName = g_strdup (D_("Open"));
358 pSubEntry->cIconName = g_strdup (GLDI_ICON_NAME_JUMP_TO);
359 pSubEntry->fill = cd_do_fill_default_entry;
360 pSubEntry->execute = _cd_do_launch_url;
361 pEntries = g_list_prepend (pEntries, pSubEntry);
362
363 pSubEntry = g_new0 (CDEntry, 1);
364 pSubEntry->cPath = g_strdup (pEntry->cPath);
365 pSubEntry->cName = g_strdup (D_("Open in new window"));
366 pSubEntry->cIconName = g_strdup (GLDI_ICON_NAME_ADD);
367 pSubEntry->fill = cd_do_fill_default_entry;
368 pSubEntry->execute = _cd_do_launch_in_new_window;
369 pEntries = g_list_prepend (pEntries, pSubEntry);
370
371 pSubEntry = g_new0 (CDEntry, 1);
372 pSubEntry->cPath = g_strdup (pEntry->cPath);
373 pSubEntry->cName = g_strdup (D_("Copy URL"));
374 pSubEntry->cIconName = g_strdup (GLDI_ICON_NAME_COPY);
375 pSubEntry->fill = cd_do_fill_default_entry;
376 pSubEntry->execute = _cd_do_copy_url;
377 pEntries = g_list_prepend (pEntries, pSubEntry);
378
379 *iNbEntries = NB_ACTIONS_ON_BOOKMARKS;
380 return pEntries;
381 }
382
_make_entry_from_item(CDBookmarkItem * pItem)383 static CDEntry *_make_entry_from_item (CDBookmarkItem *pItem)
384 {
385 CDEntry *pEntry = g_new0 (CDEntry, 1);
386 pEntry->cPath = g_strdup (pItem->cAddress);
387 pEntry->cName = g_strdup (pItem->cName);
388 pEntry->cLowerCaseName = g_strdup (pItem->cLowerCaseName);
389 if (pItem->pSubItems == NULL) // adresse
390 {
391 pEntry->cIconName = g_strdup (pItem->cIcon64);
392 pEntry->fill = _cd_do_fill_bookmark_entry;
393 pEntry->execute = _cd_do_launch_url;
394 pEntry->list = _cd_do_list_bookmarks_actions;
395 }
396 else
397 {
398 pEntry->cIconName = g_strdup ("folder");
399 pEntry->fill = cd_do_fill_default_entry;
400 pEntry->execute = NULL;
401 pEntry->list = _cd_do_list_bookmarks_folder;
402 pEntry->data = pItem;
403 }
404 return pEntry;
405 }
406
_cd_do_list_bookmarks_folder(CDEntry * pEntry,int * iNbEntries)407 static GList *_cd_do_list_bookmarks_folder (CDEntry *pEntry, int *iNbEntries)
408 {
409 int i = 0;
410 GList *pEntries = NULL;
411 CDBookmarkItem *pFolderItem = pEntry->data;
412 g_return_val_if_fail (pFolderItem != NULL, NULL);
413
414 CDBookmarkItem *pItem;
415 CDEntry *pSubEntry;
416 GString *sAllUrls = g_string_new ("");
417 GList *it;
418 for (it = pFolderItem->pSubItems; it != NULL; it = it->next)
419 {
420 pItem = it->data;
421 pSubEntry = _make_entry_from_item (pItem);
422 i ++;
423 pEntries = g_list_prepend (pEntries, pSubEntry);
424
425 g_string_append_printf (sAllUrls, "\"%s\" ", pItem->cAddress);
426 }
427
428 if (pEntries != NULL)
429 {
430 pSubEntry = g_new0 (CDEntry, 1);
431 pSubEntry->cPath = sAllUrls->str;
432 pSubEntry->cName = g_strdup (D_("Open file"));
433 pSubEntry->cIconName = g_strdup (GLDI_ICON_NAME_OPEN);
434 pSubEntry->fill = cd_do_fill_default_entry;
435 pSubEntry->execute = _cd_do_launch_all_url;
436 pEntries = g_list_prepend (pEntries, pSubEntry);
437 i ++;
438 g_string_free (sAllUrls, FALSE);
439 }
440 else
441 g_string_free (sAllUrls, TRUE);
442
443 *iNbEntries = i;
444 return pEntries;
445 }
446
447
448 ////////////
449 // SEARCH //
450 ////////////
451
_search_in_item(CDBookmarkItem * pFolderItem,const gchar * cText,int iFilter,int iNbMax,int * iNbEntries)452 static GList* _search_in_item (CDBookmarkItem *pFolderItem, const gchar *cText, int iFilter, int iNbMax, int *iNbEntries)
453 {
454 GList *pEntries = NULL;
455 int i = 0;
456 CDBookmarkItem *pItem;
457 CDEntry *pEntry;
458 GList *it;
459 for (it = pFolderItem->pSubItems; it != NULL && iNbMax > 0; it = it->next)
460 {
461 pItem = it->data;
462 if (g_strstr_len (pItem->cLowerCaseName, -1, cText))
463 {
464 pEntry = _make_entry_from_item (pItem);
465 pEntries = g_list_prepend (pEntries, pEntry);
466 i ++;
467 iNbMax --;
468 }
469 if (pItem->pSubItems != NULL)
470 {
471 int j = 0;
472 GList *pSubList = _search_in_item (pItem, cText, iFilter, iNbMax, &j);
473 i += j;
474 iNbMax -= j;
475 pEntries = g_list_concat (pEntries, pSubList);
476 }
477 }
478 *iNbEntries = i;
479 return pEntries;
480 }
search(const gchar * cText,int iFilter,gboolean bSearchAll,int * iNbEntries)481 static GList* search (const gchar *cText, int iFilter, gboolean bSearchAll, int *iNbEntries)
482 {
483 cd_debug ("%s (%s)", __func__, cText);
484 if (s_pRootItem == NULL)
485 return NULL;
486
487 int i = 0, iNbMax = (bSearchAll ? 50:3);
488 CDEntry *pEntry;
489 GList *pEntries = _search_in_item (s_pRootItem, cText, iFilter, iNbMax, &i);
490
491 if (i != 0 && ! bSearchAll)
492 {
493 pEntry = g_new0 (CDEntry, 1);
494 pEntry->cPath = NULL;
495 pEntry->cName = g_strdup (D_("Firefox bookmarks"));
496 pEntry->cIconName = g_strdup (MY_APPLET_SHARE_DATA_DIR"/firefox.png");
497 pEntry->bMainEntry = TRUE;
498 pEntry->fill = cd_do_fill_default_entry;
499 pEntry->list = cd_do_list_main_sub_entry;
500 pEntries = g_list_prepend (pEntries, pEntry);
501 i ++;
502 }
503
504 *iNbEntries = i;
505 return pEntries;
506 }
507
508
509 //////////////
510 // REGISTER //
511 //////////////
512
cd_do_register_firefox_backend(void)513 void cd_do_register_firefox_backend (void)
514 {
515 CDBackend *pBackend = g_new0 (CDBackend, 1);
516 pBackend->cName = "Firefox";
517 pBackend->bIsThreaded = FALSE;
518 pBackend->init =(CDBackendInitFunc) init;
519 pBackend->stop = (CDBackendStopFunc) stop;
520 pBackend->search = (CDBackendSearchFunc) search;
521 myData.pBackends = g_list_prepend (myData.pBackends, pBackend);
522 }
523
524
525
526 /* Chromium :
527 * {
528 "checksum": "f09d99e389bb87e956c8e4b083647898",
529 "roots": {
530 "bookmark_bar": {
531 "children": [ {
532 "date_added": "12905638722230845",
533 "id": "2",
534 "name": "Packages in \u201Cmatttbe\u201D : matttbe : Matthieu Baerts",
535 "type": "url",
536 "url": "https://launchpad.net/~matttbe/+archive/ppa/+packages"
537 }, {
538 "date_added": "12905638722232804",
539 "id": "3",
540 "name": "Packages in \u201Cexperimental-build\u201D : experimental-build : Matthieu Baerts",
541 "type": "url",
542 "url": "https://launchpad.net/~matttbe/+archive/experimental/+packages"
543 }, {
544 "date_added": "12905638722234798",
545 "id": "5",
546 "name": "Wireless Active Client MAC List",
547 "type": "url",
548 "url": "http://192.168.1.1/WClient.htm"
549 }, {
550 *
551 */
552