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