1 /*
2  * Copyright (C) 2014, Lanedo <martyn@lanedo.com>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 #include "config.h"
21 
22 #include <stdlib.h>
23 #include <errno.h>
24 
25 #ifdef __sun
26 #include <procfs.h>
27 #endif
28 
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <gio/gio.h>
32 
33 #include <libtracker-control/tracker-control.h>
34 #include <libtracker-sparql/tracker-sparql.h>
35 
36 #include "tracker-index.h"
37 #include "tracker-dbus.h"
38 
39 static gchar **reindex_mime_types;
40 static gboolean index_file;
41 static gboolean backup;
42 static gboolean restore;
43 static gboolean import;
44 static gchar **filenames;
45 
46 #define INDEX_OPTIONS_ENABLED()	  \
47 	((filenames && g_strv_length (filenames) > 0) || \
48 	 (index_file || \
49 	  backup || \
50 	  restore || \
51 	  import) || \
52 	 reindex_mime_types)
53 
54 static GOptionEntry entries[] = {
55 	{ "reindex-mime-type", 'm', 0, G_OPTION_ARG_STRING_ARRAY, &reindex_mime_types,
56 	  N_("Tell miners to reindex files which match the mime type supplied (for new extractors), use -m MIME1 -m MIME2"),
57 	  N_("MIME") },
58 	{ "file", 'f', 0, G_OPTION_ARG_NONE, &index_file,
59 	  N_("Tell miners to (re)index a given file"),
60 	  N_("FILE") },
61 	{ "backup", 'b', 0, G_OPTION_ARG_NONE, &backup,
62 	  N_("Backup current index / database to the file provided"),
63 	  NULL },
64 	{ "restore", 'o', 0, G_OPTION_ARG_NONE, &restore,
65 	  N_("Restore a database from a previous backup (see --backup)"),
66 	  NULL },
67 	{ "import", 'i', 0, G_OPTION_ARG_NONE, &import,
68 	  N_("Import a dataset from the provided file (in Turtle format)"),
69 	  NULL },
70 	{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames,
71 	  N_("FILE"),
72 	  N_("FILE") },
73 	{ NULL }
74 };
75 
76 static gboolean
has_valid_uri_scheme(const gchar * uri)77 has_valid_uri_scheme (const gchar *uri)
78 {
79 	const gchar *s;
80 
81 	s = uri;
82 
83 	if (!g_ascii_isalpha (*s)) {
84 		return FALSE;
85 	}
86 
87 	do {
88 		s++;
89 	} while (g_ascii_isalnum (*s) || *s == '+' || *s == '.' || *s == '-');
90 
91 	return (*s == ':');
92 }
93 
94 static gchar *
get_uri_from_arg(const gchar * arg)95 get_uri_from_arg (const gchar *arg)
96 {
97 	gchar *uri;
98 
99 	/* support both, URIs and local file paths */
100 	if (has_valid_uri_scheme (arg)) {
101 		uri = g_strdup (arg);
102 	} else {
103 		GFile *file;
104 
105 		file = g_file_new_for_commandline_arg (arg);
106 		uri = g_file_get_uri (file);
107 		g_object_unref (file);
108 	}
109 
110 	return uri;
111 }
112 
113 static int
reindex_mimes(void)114 reindex_mimes (void)
115 {
116 	GError *error = NULL;
117 	TrackerMinerManager *manager;
118 
119 	/* Auto-start the miners here if we need to */
120 	manager = tracker_miner_manager_new_full (TRUE, &error);
121 	if (!manager) {
122 		g_printerr (_("Could not reindex mimetypes, manager could not be created, %s"),
123 		            error ? error->message : _("No error given"));
124 		g_printerr ("\n");
125 		g_clear_error (&error);
126 		return EXIT_FAILURE;
127 	}
128 
129 	tracker_miner_manager_reindex_by_mimetype (manager, (GStrv) reindex_mime_types, &error);
130 	if (error) {
131 		g_printerr ("%s: %s\n",
132 		            _("Could not reindex mimetypes"),
133 		            error->message);
134 		g_error_free (error);
135 		return EXIT_FAILURE;
136 	}
137 
138 	g_print ("%s\n", _("Reindexing mime types was successful"));
139 	g_object_unref (manager);
140 
141 	return EXIT_SUCCESS;
142 }
143 
144 static gint
index_or_reindex_file(void)145 index_or_reindex_file (void)
146 {
147 	TrackerMinerManager *manager;
148 	GError *error = NULL;
149 	gchar **p;
150 
151 	/* Auto-start the miners here if we need to */
152 	manager = tracker_miner_manager_new_full (TRUE, &error);
153 	if (!manager) {
154 		g_printerr (_("Could not (re)index file, manager could not be created, %s"),
155 		            error ? error->message : _("No error given"));
156 		g_printerr ("\n");
157 		g_clear_error (&error);
158 		return EXIT_FAILURE;
159 	}
160 
161 	for (p = filenames; *p; p++) {
162 		GFile *file;
163 
164 		file = g_file_new_for_commandline_arg (*p);
165 		tracker_miner_manager_index_file (manager, file, NULL, &error);
166 
167 		if (error) {
168 			g_printerr ("%s: %s\n",
169 			            _("Could not (re)index file"),
170 			            error->message);
171 			g_error_free (error);
172 			return EXIT_FAILURE;
173 		}
174 
175 		g_print ("%s\n", _("(Re)indexing file was successful"));
176 		g_object_unref (file);
177 	}
178 
179 	g_object_unref (manager);
180 
181 	return EXIT_SUCCESS;
182 }
183 
184 static int
import_turtle_files(void)185 import_turtle_files (void)
186 {
187 	TrackerSparqlConnection *connection;
188 	GError *error = NULL;
189 	gchar **p;
190 
191 	connection = tracker_sparql_connection_get (NULL, &error);
192 
193 	if (!connection) {
194 		g_printerr ("%s: %s\n",
195 		            _("Could not establish a connection to Tracker"),
196 		            error ? error->message : _("No error given"));
197 		g_clear_error (&error);
198 		return EXIT_FAILURE;
199 	}
200 
201 	for (p = filenames; *p; p++) {
202 		GError *error = NULL;
203 		GFile *file;
204 
205 		g_print ("%s:'%s'\n",
206 		         _("Importing Turtle file"),
207 		         *p);
208 
209 		file = g_file_new_for_commandline_arg (*p);
210 		tracker_sparql_connection_load (connection, file, NULL, &error);
211 		g_object_unref (file);
212 
213 		if (error) {
214 			g_printerr ("  %s, %s\n",
215 			            _("Unable to import Turtle file"),
216 			            error->message);
217 
218 			g_error_free (error);
219 			continue;
220 		}
221 
222 		g_print ("  %s\n", _("Done"));
223 		g_print ("\n");
224 	}
225 
226 	g_object_unref (connection);
227 
228 	return EXIT_SUCCESS;
229 }
230 
231 static int
backup_index(void)232 backup_index (void)
233 {
234 	GDBusConnection *connection;
235 	GDBusProxy *proxy;
236 	GError *error = NULL;
237 	GVariant *v;
238 	gchar *uri;
239 
240 	if (!tracker_dbus_get_connection ("org.freedesktop.Tracker1",
241 	                                  "/org/freedesktop/Tracker1/Backup",
242 	                                  "org.freedesktop.Tracker1.Backup",
243 	                                  G_DBUS_PROXY_FLAGS_NONE,
244 	                                  &connection,
245 	                                  &proxy)) {
246 		return EXIT_FAILURE;
247 	}
248 
249 	uri = get_uri_from_arg (filenames[0]);
250 
251 	g_print ("%s\n", _("Backing up database"));
252 	g_print ("  %s\n", uri);
253 
254 	/* Backup/Restore can take some time */
255 	g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);
256 
257 	v = g_dbus_proxy_call_sync (proxy,
258 	                            "Save",
259 	                            g_variant_new ("(s)", uri),
260 	                            G_DBUS_CALL_FLAGS_NONE,
261 	                            -1,
262 	                            NULL,
263 	                            &error);
264 
265 	if (proxy) {
266 		g_object_unref (proxy);
267 	}
268 
269 	if (error) {
270 		g_critical ("%s, %s",
271 		            _("Could not backup database"),
272 		            error ? error->message : _("No error given"));
273 		g_clear_error (&error);
274 		g_free (uri);
275 
276 		return EXIT_FAILURE;
277 	}
278 
279 	if (v) {
280 		g_variant_unref (v);
281 	}
282 
283 	g_free (uri);
284 
285 	return EXIT_SUCCESS;
286 }
287 
288 static int
restore_index(void)289 restore_index (void)
290 {
291 	GDBusConnection *connection;
292 	GDBusProxy *proxy;
293 	GError *error = NULL;
294 	GVariant *v;
295 	gchar *uri;
296 
297 	if (!tracker_dbus_get_connection ("org.freedesktop.Tracker1",
298 	                                  "/org/freedesktop/Tracker1/Backup",
299 	                                  "org.freedesktop.Tracker1.Backup",
300 	                                  G_DBUS_PROXY_FLAGS_NONE,
301 	                                  &connection,
302 	                                  &proxy)) {
303 		return EXIT_FAILURE;
304 	}
305 
306 	uri = get_uri_from_arg (filenames[0]);
307 
308 	g_print ("%s\n", _("Restoring database from backup"));
309 	g_print ("  %s\n", uri);
310 
311 	/* Backup/Restore can take some time */
312 	g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);
313 
314 	v = g_dbus_proxy_call_sync (proxy,
315 	                            "Restore",
316 	                            g_variant_new ("(s)", uri),
317 	                            G_DBUS_CALL_FLAGS_NONE,
318 	                            -1,
319 	                            NULL,
320 	                            &error);
321 
322 	if (proxy) {
323 		g_object_unref (proxy);
324 	}
325 
326 	if (error) {
327 		g_critical ("%s, %s",
328 		            _("Could not backup database"),
329 		            error ? error->message : _("No error given"));
330 		g_clear_error (&error);
331 		g_free (uri);
332 
333 		return EXIT_FAILURE;
334 	}
335 
336 	if (v) {
337 		g_variant_unref (v);
338 	}
339 
340 	g_free (uri);
341 
342 	return EXIT_SUCCESS;
343 }
344 
345 static int
index_run(void)346 index_run (void)
347 {
348 	if (reindex_mime_types) {
349 		return reindex_mimes ();
350 	}
351 
352 	if (index_file) {
353 		return index_or_reindex_file ();
354 	}
355 
356 	if (import) {
357 		return import_turtle_files ();
358 	}
359 
360 	if (backup) {
361 		return backup_index ();
362 	}
363 
364 	if (restore) {
365 		return restore_index ();
366 	}
367 
368 	/* All known options have their own exit points */
369 	g_printerr("Use `tracker index --file` when giving a specific file or "
370 	           "directory to index. See `tracker help index` for more "
371 	           "information.\n");
372 
373 	return EXIT_FAILURE;
374 }
375 
376 static int
index_run_default(void)377 index_run_default (void)
378 {
379 	GOptionContext *context;
380 	gchar *help;
381 
382 	context = g_option_context_new (NULL);
383 	g_option_context_add_main_entries (context, entries, NULL);
384 	help = g_option_context_get_help (context, TRUE, NULL);
385 	g_option_context_free (context);
386 	g_printerr ("%s\n", help);
387 	g_free (help);
388 
389 	return EXIT_FAILURE;
390 }
391 
392 static gboolean
index_options_enabled(void)393 index_options_enabled (void)
394 {
395 	return INDEX_OPTIONS_ENABLED ();
396 }
397 
398 int
tracker_index(int argc,const char ** argv)399 tracker_index (int argc, const char **argv)
400 {
401 	GOptionContext *context;
402 	GError *error = NULL;
403 	const gchar *failed;
404 	gint actions = 0;
405 
406 	context = g_option_context_new (NULL);
407 	g_option_context_add_main_entries (context, entries, NULL);
408 
409 	argv[0] = "tracker index";
410 
411 	if (!g_option_context_parse (context, &argc, (char***) &argv, &error)) {
412 		g_printerr ("%s, %s\n", _("Unrecognized options"), error->message);
413 		g_error_free (error);
414 		g_option_context_free (context);
415 		return EXIT_FAILURE;
416 	}
417 
418 	g_option_context_free (context);
419 
420 	if (backup) {
421 		actions++;
422 	}
423 
424 	if (restore) {
425 		actions++;
426 	}
427 
428 	if (index_file) {
429 		actions++;
430 	}
431 
432 	if (import) {
433 		actions++;
434 	}
435 
436 	if (actions > 1) {
437 		failed = _("Only one action (--backup, --restore, --index-file or --import) can be used at a time");
438 	} else if (actions > 0 && (!filenames || g_strv_length (filenames) < 1)) {
439 		failed = _("Missing one or more files which are required");
440 	} else if ((backup || restore) && (filenames && g_strv_length (filenames) > 1)) {
441 		failed = _("Only one file can be used with --backup and --restore");
442 	} else if (actions > 0 && (reindex_mime_types && g_strv_length (reindex_mime_types) > 0)) {
443 		failed = _("Actions (--backup, --restore, --index-file and --import) can not be used with --reindex-mime-type");
444 	} else {
445 		failed = NULL;
446 	}
447 
448 	if (failed) {
449 		g_printerr ("%s\n\n", failed);
450 		return EXIT_FAILURE;
451 	}
452 
453 	if (index_options_enabled ()) {
454 		return index_run ();
455 	}
456 
457 	return index_run_default ();
458 }
459