tracker-0.16.2/utils/mtp/mtp-dummy.c

No issues found

  1 /*
  2  * Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
  3  *
  4  * This library is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Library General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) any later version.
  8  *
  9  * This library 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 GNU
 12  * General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Library General Public
 15  * License along with this library; if not, write to the
 16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 17  * Boston, MA  02110-1301, USA.
 18  */
 19 
 20 #include <stdio.h>
 21 #include <errno.h>
 22 #include <locale.h>
 23 
 24 #include <glib.h>
 25 
 26 #include <libtracker-sparql/tracker-sparql.h>
 27 
 28 #define COPY_TIMEOUT_MS 100
 29 
 30 static gint copy_rate = 1024; /* 1MByte/second */
 31 static gint n_copies = 1;
 32 static gchar **remaining;
 33 static GFile *file;
 34 static gchar *file_uri;
 35 static GFile *destdir;
 36 static gchar *destdir_uri;
 37 static gboolean use_hidden;
 38 static gboolean use_batch;
 39 
 40 /* copy_rate*1024*COPY_TIMEOUT_MS/1000 */
 41 static gint timeout_copy_rate;
 42 
 43 static gchar *buffer;
 44 static gsize  buffer_size;
 45 
 46 static GMainLoop *loop;
 47 static GList *task_list;
 48 static GList *task_list_li;
 49 
 50 TrackerSparqlConnection *connection;
 51 
 52 /* Note: don't use a GOutputStream, as that actually
 53  * creates a hidden temporary file */
 54 
 55 typedef struct {
 56 	GFile *destfile;
 57 	GFile *destfile_hidden;
 58 	FILE *fp;
 59 	gsize bytes_copied;
 60 	gsize bytes_remaining;
 61 } CopyTask;
 62 
 63 static GOptionEntry entries[] = {
 64 	{ "rate", 'r', 0, G_OPTION_ARG_INT, &copy_rate,
 65 	  "Rate of copy, in KBytes per second",
 66 	  "1024"
 67 	},
 68 	{ "copies", 'c', 0, G_OPTION_ARG_INT, &n_copies,
 69 	  "Number of copies to be done",
 70 	  "1"
 71 	},
 72 	{ "hidden", 'h', 0, G_OPTION_ARG_NONE, &use_hidden,
 73 	  "Use a hidden temp file while copying",
 74 	  NULL,
 75 	},
 76 	{ "batch", 'b', 0, G_OPTION_ARG_NONE, &use_batch,
 77 	  "Use a batch copy, using hidden temp files, and only rename the files when the batch is finished.",
 78 	  NULL,
 79 	},
 80 	{ G_OPTION_REMAINING, 0, 0,
 81 	  G_OPTION_ARG_STRING_ARRAY, &remaining,
 82 	  "file destdir",
 83 	  "FILE DESTDIR",
 84 	},
 85 	{ NULL }
 86 };
 87 
 88 static gchar **
 89 query_urns_by_url (const gchar *uri)
 90 {
 91 	GError *error = NULL;
 92 	TrackerSparqlCursor *cursor;
 93 	gchar *sparql;
 94 	gchar **urns;
 95 
 96 	sparql = g_strdup_printf ("SELECT ?urn WHERE { ?urn nie:url \"%s\" }",
 97 	                          uri);
 98 
 99 	/* Make a synchronous query to the store */
100 	cursor = tracker_sparql_connection_query (connection,
101 	                                          sparql,
102 	                                          NULL,
103 	                                          &error);
104 	if (error) {
105 		/* Some error happened performing the query, not good */
106 		g_error ("Couldn't query the Tracker Store: '%s'",
107 		         error ? error->message : "unknown error");
108 	}
109 
110 	/* Check results... */
111 	if (!cursor) {
112 		urns = NULL;
113 	} else {
114 		gchar *urns_mixed;
115 		GString *urns_string;
116 		gint i = 0;
117 
118 		urns_string = g_string_new ("");
119 		/* Iterate, synchronously, the results... */
120 		while (tracker_sparql_cursor_next (cursor, NULL, &error)) {
121 			g_string_append_printf (urns_string,
122 			                        "%s%s",
123 			                        i==0 ? "" : ";",
124 			                        tracker_sparql_cursor_get_string (cursor, 0, NULL));
125 			i++;
126 		}
127 
128 		if (error) {
129 			g_error ("Error iterating cursor: %s",
130 			         error->message);
131 		}
132 
133 		urns_mixed = g_string_free (urns_string, FALSE);
134 		urns = g_strsplit (urns_mixed, ";", -1);
135 		g_free (urns_mixed);
136 		g_object_unref (cursor);
137 	}
138 
139 	g_free (sparql);
140 
141 	return urns;
142 }
143 
144 static void
145 update_store (const gchar *sparql)
146 {
147 	GError *error = NULL;
148 
149 	/* Run a synchronous update query */
150 	tracker_sparql_connection_update (connection,
151 	                                  sparql,
152 	                                  G_PRIORITY_DEFAULT,
153 	                                  NULL,
154 	                                  &error);
155 
156 	if (error) {
157 		/* Some error happened performing the query, not good */
158 		g_error ("Couldn't update store for '%s': %s",
159 		         sparql,
160 		         error ? error->message : "unknown error");
161 	}
162 }
163 
164 static void
165 insert_element_in_store (GFile *destfile)
166 {
167 	gchar *sparql;
168 	gchar *uri;
169 
170 	uri = g_file_get_uri (destfile);
171 	sparql = g_strdup_printf ("DELETE { ?file a rdfs:Resource} "
172 	                          "WHERE { ?file nie:url \"%s\" } "
173 	                          "INSERT { _:x a nfo:FileDataObject;"
174 	                          "             nie:url \"%s\" }",
175 	                          uri, uri);
176 
177 	g_print ("  Updating store with new resource '%s'\n", uri);
178 
179 	update_store (sparql);
180 
181 	g_free (uri);
182 	g_free (sparql);
183 }
184 
185 static void
186 replace_url_element_in_store (GFile *sourcefile, GFile *destfile)
187 {
188 	gchar *sparql;
189 	gchar *source_uri;
190 	gchar *dest_uri;
191 	gchar **urns;
192 
193 	source_uri = g_file_get_uri (sourcefile);
194 	dest_uri = g_file_get_uri (destfile);
195 
196 
197 	urns = query_urns_by_url (source_uri);
198 	if (!urns || g_strv_length (urns) != 1) {
199 		g_error ("Expected only 1 item with url '%s' at this point! (got %d)",
200 		         source_uri,
201 		         urns ? g_strv_length (urns) : 0);
202 	}
203 
204 	sparql = g_strdup_printf ("DELETE { <%s> nie:url ?url} WHERE { <%s> nie:url ?url } "
205 	                          "INSERT INTO <%s> { <%s> nie:url \"%s\" }",
206 	                          urns[0], urns[0], urns[0], urns[0], dest_uri);
207 
208 	g_print ("  Changing nie:url from '%s' to '%s'\n", source_uri, dest_uri);
209 
210 	update_store (sparql);
211 
212 	g_strfreev (urns);
213 	g_free (source_uri);
214 	g_free (dest_uri);
215 	g_free (sparql);
216 }
217 
218 static gboolean
219 context_init (gint    argc,
220               gchar **argv)
221 {
222 	GOptionContext *context;
223 	gint n_remaining;
224 	gchar *file_path;
225 	GError *error = NULL;
226 
227 	/* Setup context */
228 	context = g_option_context_new ("- Simulate MTP daemon");
229 	g_option_context_add_main_entries (context, entries, NULL);
230 	g_option_context_parse (context, &argc, &argv, NULL);
231 	g_option_context_free (context);
232 
233 	/* Check input arguments */
234 	n_remaining = remaining ? g_strv_length (remaining) : 0;
235 	if (n_remaining != 2) {
236 		g_printerr ("You must provide FILE and DESTDIR\n");
237 		return FALSE;
238 	}
239 
240 	/* Get and check input file */
241 	file = g_file_new_for_commandline_arg (remaining[0]);
242 	file_uri = g_file_get_uri (file);
243 	file_path = g_file_get_path (file);
244 	if (g_file_query_file_type (file,
245 	                            G_FILE_QUERY_INFO_NONE,
246 	                            NULL) !=  G_FILE_TYPE_REGULAR) {
247 		g_printerr ("File '%s' is not a valid regular file\n",
248 		            file_uri);
249 		return FALSE;
250 	}
251 
252 	/* Get destination directory */
253 	destdir = g_file_new_for_commandline_arg (remaining[1]);
254 	destdir_uri = g_file_get_uri (destdir);
255 	if (g_file_query_file_type (destdir,
256 	                            G_FILE_QUERY_INFO_NONE,
257 	                            NULL) !=  G_FILE_TYPE_DIRECTORY) {
258 		g_printerr ("Destination path '%s' is not a valid directory\n",
259 		            destdir_uri);
260 		return FALSE;
261 	}
262 
263 	/* Check n_copies */
264 	if (n_copies == 0) {
265 		g_printerr ("Number of copies must be greater than 0\n");
266 		return FALSE;
267 	}
268 
269 	/* Check rate */
270 	if (copy_rate == 0) {
271 		g_printerr ("Copy rate must be greater than 0\n");
272 		return FALSE;
273 	}
274 
275 	/* copy_rate*1024*COPY_TIMEOUT_MS/1000 */
276 	timeout_copy_rate = copy_rate * 1024.0 * COPY_TIMEOUT_MS / 1000.0;
277 
278 	/* Read all input file contents */
279 	if (!g_file_get_contents (file_path,
280 	                          &buffer,
281 	                          &buffer_size,
282 	                          &error)) {
283 		g_error ("Couldn't load file '%s' contents: %s",
284 		         file_uri,
285 		         error ? error->message : "unknown error");
286 	}
287 
288 	/* Get connection */
289 	connection = tracker_sparql_connection_get (NULL, &error);
290 	if (!connection) {
291 		/* Some error happened performing the query, not good */
292 		g_error ("Couldn't get sparql connection: %s",
293 		         error ? error->message : "unknown error");
294 	}
295 
296 	g_print ("\
297 Simulating MTP daemon with:\n\
298   * File:    %s (%" G_GSIZE_FORMAT " bytes)\n\
299   * Destdir: %s\n\
300   * Copies:  %d\n\
301   * Rate:    %d KBytes/s (%d bytes every %d ms)\n\
302   * Mode:    %s\n",
303 	         file_uri,
304 	         buffer_size,
305 	         destdir_uri,
306 	         n_copies,
307 	         copy_rate,
308 	         timeout_copy_rate,
309 	         COPY_TIMEOUT_MS,
310 	         use_batch ? "Hidden & Batch" : use_hidden ? "Hidden" : "Normal");
311 
312 	return TRUE;
313 }
314 
315 static void
316 context_deinit (void)
317 {
318 	g_object_unref (file);
319 	g_free (file_uri);
320 	g_object_unref (destdir);
321 	g_free (destdir_uri);
322 	g_free (buffer);
323 	g_object_unref (connection);
324 }
325 
326 static gboolean
327 task_run_cb (gpointer data)
328 {
329 	CopyTask *current;
330 	gsize n_write;
331 
332 	/* Get current task */
333 	current = task_list_li ? task_list_li->data : NULL;
334 
335 	/* Stop looping? */
336 	if (!current) {
337 		g_print ("\n\nNo more tasks to run, finishing...\n");
338 		g_main_loop_quit (loop);
339 		return FALSE;
340 	}
341 
342 	/* If we just started copying it... */
343 	if (!current->fp) {
344 		gchar *destfile_path;
345 
346 		g_print ("Running new copy task...\n");
347 
348 		destfile_path = (use_hidden || use_batch ?
349 		                 g_file_get_path (current->destfile_hidden) :
350 		                 g_file_get_path (current->destfile));
351 
352 		/* Get file pointer */
353 		current->fp = fopen (destfile_path, "w");
354 		if (!current->fp)
355 		{
356 			g_error ("Couldn't get file pointer: %s",
357 			         g_strerror (errno));
358 		}
359 
360 		/* Create new item in the store right away */
361 		insert_element_in_store (use_hidden || use_batch ?
362 		                         current->destfile_hidden :
363 		                         current->destfile);
364 		g_free (destfile_path);
365 	}
366 
367 	/* Copy bytes */
368 	n_write = MIN (current->bytes_remaining, timeout_copy_rate);
369 	if (fwrite (&buffer[current->bytes_copied],
370 	            1,
371 	            n_write,
372 	            current->fp) != n_write) {
373 		g_error ("Couldn't write in output file: %s",
374 		         g_strerror (errno));
375 	}
376 
377 	current->bytes_remaining -= n_write;
378 	current->bytes_copied += n_write;
379 
380 	/* Finished with this task? */
381 	if (current->bytes_remaining == 0) {
382 		fclose (current->fp);
383 		current->fp = NULL;
384 
385 		if (use_hidden && !use_batch) {
386 			GError *error = NULL;
387 
388 			/* Change nie:url in the store */
389 			replace_url_element_in_store (current->destfile_hidden,
390 			                              current->destfile);
391 
392 			/* Copying finished, now MOVE to the final path */
393 			if (!g_file_move (current->destfile_hidden,
394 			                  current->destfile,
395 			                  G_FILE_COPY_OVERWRITE,
396 			                  NULL,
397 			                  NULL,
398 			                  NULL,
399 			                  &error)) {
400 				g_error ("Couldn't copy file to the final destination: %s",
401 				         error ? error->message : "unknown error");
402 			}
403 		}
404 
405 		/* Setup next task */
406 		task_list_li = g_list_next (task_list_li);
407 
408 		/* If this is the LAST task the one we just processed, perform the
409 		 * batch renaming if required. */
410 		if (!task_list_li && use_batch) {
411 			for (task_list_li = task_list;
412 			     task_list_li;
413 			     task_list_li = g_list_next (task_list_li)) {
414 				GError *error = NULL;
415 
416 				current = task_list_li->data;
417 				/* Change nie:url in the store */
418 				replace_url_element_in_store (current->destfile_hidden,
419 				                              current->destfile);
420 
421 				/* Copying finished, now MOVE to the final path */
422 				if (!g_file_move (current->destfile_hidden,
423 				                  current->destfile,
424 				                  G_FILE_COPY_OVERWRITE,
425 				                  NULL,
426 				                  NULL,
427 				                  NULL,
428 				                  &error)) {
429 					g_error ("Couldn't copy file to the final destination (batch): %s",
430 					         error ? error->message : "unknown error");
431 				}
432 			}
433 		}
434 	}
435 
436 	return TRUE;
437 }
438 
439 static void
440 setup_tasks (void)
441 {
442 	gint i;
443 	gchar *input_file_basename;
444 
445 	input_file_basename = g_file_get_basename (file);
446 
447 	for (i=n_copies-1; i>=0; i--) {
448 		CopyTask *task;
449 		gchar *basename;
450 
451 		basename = g_strdup_printf ("file-copy-%d-%s",
452 		                            i,
453 		                            input_file_basename);
454 
455 		task = g_new0 (CopyTask, 1);
456 		task->destfile = g_file_get_child (destdir, basename);
457 		task->bytes_remaining = buffer_size;
458 
459 		if (use_hidden || use_batch) {
460 			gchar *basename_hidden;
461 
462 			basename_hidden = g_strdup_printf (".mtp-dummy.file-copy-%d-%s",
463 			                                   i,
464 			                                   input_file_basename);
465 			task->destfile_hidden = g_file_get_child (destdir, basename_hidden);
466 			g_free (basename_hidden);
467 		}
468 
469 		task_list = g_list_prepend (task_list, task);
470 
471 		g_free (basename);
472 	}
473 
474 	/* Setup first task */
475 	task_list_li = task_list;
476 
477 	/* Timeout every N milliseconds */
478 	g_timeout_add (COPY_TIMEOUT_MS,
479 	               task_run_cb,
480 	               NULL);
481 }
482 
483 static void
484 check_duplicates_for_uri (const gchar *uri)
485 {
486 	gchar **urns;
487 
488 	urns = query_urns_by_url (uri);
489 
490 	/* Check results... */
491 	if (!urns) {
492 		g_print ("  For '%s' found 0 results!\n", uri);
493 	} else {
494 		gint i;
495 
496 		g_print ("  A total of '%d results where found for '%s':\n",
497 		         g_strv_length (urns), uri);
498 		for (i=0; urns[i]; i++) {
499 			g_print ("    [%d]: %s\n", i, urns[i]);
500 		}
501 		g_strfreev (urns);
502 	}
503 }
504 
505 static void
506 check_duplicates (void)
507 {
508 	g_print ("\nChecking duplicates...\n");
509 
510 	task_list_li = task_list;
511 
512 	while (task_list_li) {
513 		CopyTask *current;
514 		gchar *uri;
515 
516 		current = task_list_li->data;
517 		uri = g_file_get_uri (current->destfile);
518 
519 		check_duplicates_for_uri (uri);
520 
521 		g_free (uri);
522 		g_object_unref (current->destfile);
523 		g_free (current);
524 		task_list_li = g_list_next (task_list_li);;
525 	}
526 
527 	g_list_free (task_list);
528 }
529 
530 int main (int argc, char **argv)
531 {
532 	/* Initialize locale support! */
533 	setlocale (LC_ALL, "");
534 
535 	/* Initialize context */
536 	if (!context_init (argc, argv)) {
537 		g_printerr ("Couldn't setup context, exiting.");
538 		return -1;
539 	}
540 
541 	/* Setup tasks */
542 	setup_tasks ();
543 
544 	/* Run */
545 	g_print ("\nStarting...\n\n");
546 	loop = g_main_loop_new (NULL, FALSE);
547 	g_main_loop_run (loop);
548 	g_main_loop_unref (loop);
549 
550 	/* Check for duplicates and cleanup copied files */
551 	check_duplicates ();
552 
553 	context_deinit ();
554 	return 0;
555 }