1 /*
2     FUSE: Filesystem in Userspace
3     Copyright (C) 2001-2005  Miklos Szeredi <miklos@szeredi.hu>
4 
5     This program can be distributed under the terms of the GNU GPL.
6     See the file COPYING.
7 */
8 
9 #include <mtpfs.h>
10 #include <glib/gprintf.h>
11 #include <stdlib.h>		/* strtoul() */
12 
13 #if DEBUG
14 #define STRINGIFY(x) #x
15 #define TOSTRING(x) STRINGIFY(x)
16 #define DBG(a...) {g_printf( "[" __FILE__ ":" TOSTRING(__LINE__) "] " a );g_printf("\n");}
17 #else
18 #define DBG(a...)
19 #endif
20 
21 #if DEBUG
22 static void
dump_mtp_error()23 dump_mtp_error ()
24 {
25   LIBMTP_Dump_Errorstack (device);
26   LIBMTP_Clear_Errorstack (device);
27 }
28 #else
29 #define dump_mtp_error()
30 #endif
31 
32 #define enter_lock(a...)       do { DBG("lock"); DBG(a); g_mutex_lock(&device_lock); } while(0)
33 #define return_unlock(a)       do { DBG("return unlock"); g_mutex_unlock(&device_lock); return a; } while(0)
34 
35 void
free_files(LIBMTP_file_t * filelist)36 free_files (LIBMTP_file_t * filelist)
37 {
38   LIBMTP_file_t *file = filelist, *tmp;
39   while (file)
40     {
41       tmp = file;
42       file = file->next;
43       LIBMTP_destroy_file_t (tmp);
44     }
45 }
46 
47 void
free_playlists(LIBMTP_playlist_t * pl)48 free_playlists (LIBMTP_playlist_t * pl)
49 {
50   LIBMTP_playlist_t *playlist = pl, *tmp;
51   while (playlist)
52     {
53       tmp = playlist;
54       playlist = playlist->next;
55       LIBMTP_destroy_playlist_t (tmp);
56     }
57 }
58 
59 void
check_files()60 check_files ()
61 {
62   if (files_changed)
63     {
64       DBG ("Refreshing Filelist");
65       LIBMTP_file_t *newfiles = NULL;
66       if (files)
67 	free_files (files);
68       newfiles = LIBMTP_Get_Filelisting_With_Callback (device, NULL, NULL);
69       files = newfiles;
70       newfiles = NULL;
71       files_changed = FALSE;
72       //check_lost_files ();
73       DBG ("Refreshing Filelist exiting");
74     }
75 }
76 
77 static void
check_lost_files()78 check_lost_files ()
79 {
80   int last_parent_id = -1;
81   gboolean last_parent_found = FALSE;
82   LIBMTP_file_t *item;
83 
84   if (lostfiles != NULL)
85     g_slist_free (lostfiles);
86 
87   lostfiles = NULL;
88   for (item = files; item != NULL; item = item->next)
89     {
90       gboolean parent_found;
91 
92       if (last_parent_id == -1 || last_parent_id != item->parent_id)
93 	{
94 	  if (item->parent_id == 0)
95 	    {
96 	      parent_found = TRUE;
97 	    }
98 	  else
99 	    {
100 	      int i;
101 	      for (i = 0; i < 4; i++)
102 		{
103 		  if (storageArea[i].folders != NULL)
104 		    {
105 		      if (LIBMTP_Find_Folder
106 			  (storageArea[i].folders, item->parent_id) != NULL)
107 			{
108 			  parent_found = FALSE;
109 			}
110 		    }
111 		}
112 	    }
113 	  last_parent_id = item->parent_id;
114 	  last_parent_found = parent_found;
115 	}
116       else
117 	{
118 	  parent_found = last_parent_found;
119 	}
120       DBG ("MTPFS checking for lost files %s, parent %d - %s",
121 	   item->filename, last_parent_id, (parent_found ? "FALSE" : "TRUE"));
122       if (parent_found == FALSE)
123 	{
124 	  lostfiles = g_slist_append (lostfiles, item);
125 	}
126     }
127   DBG ("MTPFS checking for lost files exit found %d lost tracks",
128        g_slist_length (lostfiles));
129 }
130 
131 void
check_folders()132 check_folders ()
133 {
134   int i;
135   for (i = 0; i < 4; i++)
136     {
137       if (storageArea[i].folders_changed)
138 	{
139 	  DBG ("Refreshing Folderlist %d-%s", i,
140 	       storageArea[i].storage->StorageDescription);
141 	  LIBMTP_folder_t *newfolders = NULL;
142 	  if (storageArea[i].folders)
143 	    {
144 	      LIBMTP_destroy_folder_t (storageArea[i].folders);
145 	    }
146 	  newfolders =
147 	    LIBMTP_Get_Folder_List_For_Storage (device,
148 						storageArea[i].storage->id);
149 	  storageArea[i].folders = newfolders;
150 	  newfolders = NULL;
151 	  storageArea[i].folders_changed = FALSE;
152 	}
153     }
154 }
155 
156 void
check_playlists()157 check_playlists ()
158 {
159   if (playlists_changed)
160     {
161       DBG ("Refreshing Playlists");
162       LIBMTP_playlist_t *newplaylists;
163       if (playlists)
164 	free_playlists (playlists);
165       newplaylists = LIBMTP_Get_Playlist_List (device);
166       playlists = newplaylists;
167       playlists_changed = FALSE;
168     }
169 }
170 
171 int
save_playlist(const char * path,struct fuse_file_info * fi)172 save_playlist (const char *path, struct fuse_file_info *fi)
173 {
174   DBG ("save_playlist");
175   int ret = 0;
176 
177   LIBMTP_playlist_t *playlist;
178   FILE *file = NULL;
179   char item_path[1024];
180   uint32_t item_id = 0;
181   uint32_t *tracks;
182   gchar **fields;
183   GSList *tmplist = NULL;
184 
185   fields = g_strsplit (path, "/", -1);
186   gchar *playlist_name;
187   playlist_name = g_strndup (fields[2], strlen (fields[2]) - 4);
188   DBG ("Adding:%s", playlist_name);
189   g_strfreev (fields);
190 
191   playlist = LIBMTP_new_playlist_t ();
192   playlist->name = g_strdup (playlist_name);
193 
194   file = fdopen (fi->fh, "r");
195   while (fgets (item_path, sizeof (item_path) - 1, file) != NULL)
196     {
197       g_strchomp (item_path);
198       item_id = parse_path (item_path);
199       if (item_id != -1)
200 	{
201 	  tmplist = g_slist_append (tmplist, GUINT_TO_POINTER (item_id));
202 	  DBG ("Adding to tmplist:%d", item_id);
203 	}
204     }
205   playlist->no_tracks = g_slist_length (tmplist);
206   tracks = g_malloc (playlist->no_tracks * sizeof (uint32_t));
207   int i;
208   for (i = 0; i < playlist->no_tracks; i++)
209     {
210       tracks[i] = (uint32_t) GPOINTER_TO_UINT (g_slist_nth_data (tmplist, i));
211       DBG ("Adding:%d-%d", i, tracks[i]);
212     }
213   playlist->tracks = tracks;
214   DBG ("Total:%d", playlist->no_tracks);
215 
216   int playlist_id = 0;
217   LIBMTP_playlist_t *tmp_playlist;
218   check_playlists ();
219   tmp_playlist = playlists;
220   while (tmp_playlist != NULL)
221     {
222       if (g_ascii_strcasecmp (tmp_playlist->name, playlist_name) == 0)
223 	{
224 	  playlist_id = playlist->playlist_id;
225 	}
226       tmp_playlist = tmp_playlist->next;
227     }
228 
229   if (playlist_id > 0)
230     {
231       DBG ("Update playlist %d", playlist_id);
232       playlist->playlist_id = playlist_id;
233       ret = LIBMTP_Update_Playlist (device, playlist);
234     }
235   else
236     {
237       DBG ("New playlist");
238       ret = LIBMTP_Create_New_Playlist (device, playlist);
239     }
240   playlists_changed = TRUE;
241   return ret;
242 }
243 
244 /* Find the file type based on extension */
245 static LIBMTP_filetype_t
find_filetype(const gchar * filename)246 find_filetype (const gchar * filename)
247 {
248   DBG ("find_filetype");
249   gchar **fields;
250   fields = g_strsplit (filename, ".", -1);
251   gchar *ptype;
252   ptype = g_strdup (fields[g_strv_length (fields) - 1]);
253   g_strfreev (fields);
254   LIBMTP_filetype_t filetype;
255 
256   // This need to be kept constantly updated as new file types arrive.
257   if (!g_ascii_strncasecmp (ptype, "wav", 3))
258     {
259       filetype = LIBMTP_FILETYPE_WAV;
260     }
261   else if (!g_ascii_strncasecmp (ptype, "mp3", 3))
262     {
263       filetype = LIBMTP_FILETYPE_MP3;
264     }
265   else if (!g_ascii_strncasecmp (ptype, "wma", 3))
266     {
267       filetype = LIBMTP_FILETYPE_WMA;
268     }
269   else if (!g_ascii_strncasecmp (ptype, "ogg", 3))
270     {
271       filetype = LIBMTP_FILETYPE_OGG;
272     }
273   else if (!g_ascii_strncasecmp (ptype, "aa", 2))
274     {
275       filetype = LIBMTP_FILETYPE_AUDIBLE;
276     }
277   else if (!g_ascii_strncasecmp (ptype, "mp4", 3))
278     {
279       filetype = LIBMTP_FILETYPE_MP4;
280     }
281   else if (!g_ascii_strncasecmp (ptype, "wmv", 3))
282     {
283       filetype = LIBMTP_FILETYPE_WMV;
284     }
285   else if (!g_ascii_strncasecmp (ptype, "avi", 3))
286     {
287       filetype = LIBMTP_FILETYPE_AVI;
288     }
289   else if (!g_ascii_strncasecmp (ptype, "mpeg", 4)
290 	   || !g_ascii_strncasecmp (ptype, "mpg", 3))
291     {
292       filetype = LIBMTP_FILETYPE_MPEG;
293     }
294   else if (!g_ascii_strncasecmp (ptype, "asf", 3))
295     {
296       filetype = LIBMTP_FILETYPE_ASF;
297     }
298   else if (!g_ascii_strncasecmp (ptype, "qt", 2)
299 	   || !g_ascii_strncasecmp (ptype, "mov", 3))
300     {
301       filetype = LIBMTP_FILETYPE_QT;
302     }
303   else if (!g_ascii_strncasecmp (ptype, "wma", 3))
304     {
305       filetype = LIBMTP_FILETYPE_WMA;
306     }
307   else if (!g_ascii_strncasecmp (ptype, "jpg", 3)
308 	   || !g_ascii_strncasecmp (ptype, "jpeg", 4))
309     {
310       filetype = LIBMTP_FILETYPE_JPEG;
311     }
312   else if (!g_ascii_strncasecmp (ptype, "jfif", 4))
313     {
314       filetype = LIBMTP_FILETYPE_JFIF;
315     }
316   else if (!g_ascii_strncasecmp (ptype, "tif", 3)
317 	   || !g_ascii_strncasecmp (ptype, "tiff", 4))
318     {
319       filetype = LIBMTP_FILETYPE_TIFF;
320     }
321   else if (!g_ascii_strncasecmp (ptype, "bmp", 3))
322     {
323       filetype = LIBMTP_FILETYPE_BMP;
324     }
325   else if (!g_ascii_strncasecmp (ptype, "gif", 3))
326     {
327       filetype = LIBMTP_FILETYPE_GIF;
328     }
329   else if (!g_ascii_strncasecmp (ptype, "pic", 3)
330 	   || !g_ascii_strncasecmp (ptype, "pict", 4))
331     {
332       filetype = LIBMTP_FILETYPE_PICT;
333     }
334   else if (!g_ascii_strncasecmp (ptype, "png", 3))
335     {
336       filetype = LIBMTP_FILETYPE_PNG;
337     }
338   else if (!g_ascii_strncasecmp (ptype, "wmf", 3))
339     {
340       filetype = LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT;
341     }
342   else if (!g_ascii_strncasecmp (ptype, "ics", 3))
343     {
344       filetype = LIBMTP_FILETYPE_VCALENDAR2;
345     }
346   else if (!g_ascii_strncasecmp (ptype, "exe", 3)
347 	   || !g_ascii_strncasecmp (ptype, "com", 3)
348 	   || !g_ascii_strncasecmp (ptype, "bat", 3)
349 	   || !g_ascii_strncasecmp (ptype, "dll", 3)
350 	   || !g_ascii_strncasecmp (ptype, "sys", 3))
351     {
352       filetype = LIBMTP_FILETYPE_WINEXEC;
353     }
354   else if (!g_ascii_strncasecmp (ptype, "txt", 3))
355     {
356       filetype = LIBMTP_FILETYPE_TEXT;
357     }
358   else if (!g_ascii_strncasecmp (ptype, "htm", 3)
359 	   || !g_ascii_strncasecmp (ptype, "html", 4))
360     {
361       filetype = LIBMTP_FILETYPE_HTML;
362     }
363   else if (!g_ascii_strncasecmp (ptype, "bin", 3))
364     {
365       filetype = LIBMTP_FILETYPE_FIRMWARE;
366     }
367   else if (!g_ascii_strncasecmp (ptype, "aac", 3))
368     {
369       filetype = LIBMTP_FILETYPE_AAC;
370     }
371   else if (!g_ascii_strncasecmp (ptype, "flac", 4)
372 	   || !g_ascii_strncasecmp (ptype, "fla", 3))
373     {
374       filetype = LIBMTP_FILETYPE_FLAC;
375     }
376   else if (!g_ascii_strncasecmp (ptype, "mp2", 3))
377     {
378       filetype = LIBMTP_FILETYPE_MP2;
379     }
380   else if (!g_ascii_strncasecmp (ptype, "m4a", 3))
381     {
382       filetype = LIBMTP_FILETYPE_M4A;
383     }
384   else if (!g_ascii_strncasecmp (ptype, "doc", 3))
385     {
386       filetype = LIBMTP_FILETYPE_DOC;
387     }
388   else if (!g_ascii_strncasecmp (ptype, "xml", 3))
389     {
390       filetype = LIBMTP_FILETYPE_XML;
391     }
392   else if (!g_ascii_strncasecmp (ptype, "xls", 3))
393     {
394       filetype = LIBMTP_FILETYPE_XLS;
395     }
396   else if (!g_ascii_strncasecmp (ptype, "ppt", 3))
397     {
398       filetype = LIBMTP_FILETYPE_PPT;
399     }
400   else if (!g_ascii_strncasecmp (ptype, "mht", 3))
401     {
402       filetype = LIBMTP_FILETYPE_MHT;
403     }
404   else if (!g_ascii_strncasecmp (ptype, "jp2", 3))
405     {
406       filetype = LIBMTP_FILETYPE_JP2;
407     }
408   else if (!g_ascii_strncasecmp (ptype, "jpx", 3))
409     {
410       filetype = LIBMTP_FILETYPE_JPX;
411     }
412   else
413     {
414       DBG ("Sorry, file type \"%s\" is not yet supported", ptype);
415       DBG ("Tagging as unknown file type.");
416       filetype = LIBMTP_FILETYPE_UNKNOWN;
417     }
418   g_free (ptype);
419   return filetype;
420 }
421 
422 static int
find_storage(const gchar * path)423 find_storage (const gchar * path)
424 {
425   int i;
426   DBG ("find_storage:%s", path);
427   for (i = 0; i < 4; i++)
428     {
429       if (storageArea[i].storage != NULL)
430 	{
431 	  int maxlen = strlen (storageArea[i].storage->StorageDescription);
432 	  if (strlen (path + 1) < maxlen)
433 	    maxlen = strlen (path + 1);
434 	  if (strncmp
435 	      (storageArea[i].storage->StorageDescription,
436 	       path + 1, maxlen) == 0)
437 	    {
438 	      DBG ("%s found as %d",
439 		   storageArea[i].storage->StorageDescription, i);
440 	      return i;
441 	    }
442 	}
443     }
444   DBG ("could not find storage for %s", path);
445   return -1;
446 }
447 
448 static int
lookup_folder_id(LIBMTP_folder_t * folderlist,gchar * path,gchar * parent)449 lookup_folder_id (LIBMTP_folder_t * folderlist, gchar * path, gchar * parent)
450 {
451   DBG ("lookup_folder_id %s,%s", path, parent);
452   int ret = -1;
453   if (folderlist == NULL)
454     {
455       return -1;
456     }
457   gchar *mypath;
458   mypath = path;
459   check_folders ();
460   if (parent == NULL)
461     {
462       if (g_strrstr (path + 1, "/") == NULL)
463 	{
464 	  DBG ("Storage dir");
465 	  return -2;
466 	}
467       else
468 	{
469 	  DBG ("Strip storage area name");
470 	  mypath = strstr (path + 1, "/");
471 	  parent = "";
472 	}
473     }
474 
475   gchar *current;
476   current = g_strconcat (parent, "/", folderlist->name, NULL);
477   LIBMTP_devicestorage_t *storage;
478 
479   DBG ("compare %s,%s", mypath, current);
480   if (g_ascii_strcasecmp (mypath, current) == 0)
481     {
482       ret = folderlist->folder_id;
483     }
484   else if (g_ascii_strncasecmp (mypath, current, strlen (current)) == 0)
485     {
486       ret = lookup_folder_id (folderlist->child, mypath, current);
487     }
488 
489   if (ret == -1)
490     {
491       ret = lookup_folder_id (folderlist->sibling, mypath, parent);
492     }
493   g_free (current);
494   return ret;
495 }
496 
497 static int
parse_path(const gchar * path)498 parse_path (const gchar * path)
499 {
500   DBG ("parse_path:%s", path);
501   int res;
502   int item_id = -1;
503   int i;
504   // Check cached files first
505   GSList *item;
506   item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
507   if (item != NULL)
508     return 0;
509 
510   // Check Playlists
511   if (strncmp ("/Playlists", path, 10) == 0)
512     {
513       LIBMTP_playlist_t *playlist;
514 
515       res = -ENOENT;
516       check_playlists ();
517       playlist = playlists;
518       while (playlist != NULL)
519 	{
520 	  gchar *tmppath;
521 	  tmppath = g_strconcat ("/Playlists/", playlist->name, ".m3u", NULL);
522 	  if (g_ascii_strcasecmp (path, tmppath) == 0)
523 	    {
524 	      res = playlist->playlist_id;
525 	      g_free (tmppath);
526 	      break;
527 	    }
528 	  g_free (tmppath);
529 	  playlist = playlist->next;
530 	}
531       return res;
532     }
533   // Check lost+found
534   if (strncmp ("/lost+found", path, 11) == 0)
535     {
536       GSList *item;
537       gchar *filename = g_path_get_basename (path);
538 
539       res = -ENOENT;
540       for (item = lostfiles; item != NULL; item = g_slist_next (item))
541 	{
542 	  LIBMTP_file_t *file = (LIBMTP_file_t *) item->data;
543 
544 	  if (strcmp (file->filename, filename) == 0)
545 	    {
546 	      res = file->item_id;
547 	      break;
548 	    }
549 	}
550       g_free (filename);
551       return res;
552     }
553   // Check device
554   LIBMTP_folder_t *folder;
555   gchar **fields;
556   gchar *directory;
557   directory = (gchar *) g_malloc (strlen (path) + 1);
558   directory = strcpy (directory, "");
559   fields = g_strsplit (path, "/", -1);
560   res = -ENOENT;
561   int storageid;
562   storageid = find_storage (path);
563   if (storageid < 0)
564     {
565       return res;
566     }
567   for (i = 0; fields[i] != NULL; i++)
568     {
569       if (strlen (fields[i]) > 0)
570 	{
571 	  if (fields[i + 1] != NULL)
572 	    {
573 	      directory = strcat (directory, "/");
574 	      directory = strcat (directory, fields[i]);
575 	    }
576 	  else
577 	    {
578 	      check_folders ();
579 	      folder = storageArea[storageid].folders;
580 	      int folder_id = 0;
581 	      if (strcmp (directory, "") != 0)
582 		{
583 		  folder_id = lookup_folder_id (folder, directory, NULL);
584 		}
585 	      DBG ("parent id:%d:%s", folder_id, directory);
586 	      LIBMTP_file_t *file;
587 	      check_files ();
588 	      file = files;
589 	      while (file != NULL)
590 		{
591 		  if ((file->parent_id == folder_id) ||
592 		      (folder_id == -2
593 		       && (file->parent_id == 0)
594 		       && (file->storage_id ==
595 			   storageArea[storageid].storage->id)))
596 		    {
597 		      if (file->filename == NULL)
598 			DBG ("MTPFS filename NULL");
599 		      if (file->filename != NULL
600 			  &&
601 			  g_ascii_strcasecmp (file->filename, fields[i]) == 0)
602 			{
603 			  DBG ("found:%d:%s", file->item_id, file->filename);
604 
605 			  item_id = file->item_id;
606 			  break;	// found!
607 			}
608 		    }
609 		  file = file->next;
610 		}
611 	      if (item_id < 0)
612 		{
613 		  directory = strcat (directory, "/");
614 		  directory = strcat (directory, fields[i]);
615 		  item_id = lookup_folder_id (folder, directory, NULL);
616 		  res = item_id;
617 		  break;
618 		}
619 	      else
620 		{
621 		  res = item_id;
622 		  break;
623 		}
624 	    }
625 	}
626     }
627   g_free (directory);
628   g_strfreev (fields);
629   DBG ("parse_path exiting:%s - %d", path, res);
630   return res;
631 }
632 
633 static int
mtpfs_release(const char * path,struct fuse_file_info * fi)634 mtpfs_release (const char *path, struct fuse_file_info *fi)
635 {
636   enter_lock ("release: %s", path);
637   // Check cached files first
638   GSList *item;
639   item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
640 
641   if (item != NULL)
642     {
643       if (strncmp ("/Playlists/", path, 11) == 0)
644 	{
645 	  save_playlist (path, fi);
646 	  close (fi->fh);
647 	  return_unlock (0);
648 	}
649       else
650 	{
651 	  //find parent id
652 	  gchar *filename = g_strdup ("");
653 	  gchar **fields;
654 	  gchar *directory;
655 	  directory = (gchar *) g_malloc (strlen (path) + 1);
656 	  directory = strcpy (directory, "/");
657 	  fields = g_strsplit (path, "/", -1);
658 	  int i;
659 	  int parent_id = 0;
660 	  int storageid;
661 	  storageid = find_storage (path);
662 	  if (storageid < 0)
663 	    {
664 	      return_unlock (-ENOENT);
665 	    }
666 	  for (i = 0; fields[i] != NULL; i++)
667 	    {
668 	      if (strlen (fields[i]) > 0)
669 		{
670 		  if (fields[i + 1] == NULL)
671 		    {
672 		      gchar *tmp = g_strndup (directory,
673 					      strlen (directory) - 1);
674 		      parent_id =
675 			lookup_folder_id (storageArea
676 					  [storageid].folders, tmp, NULL);
677 		      g_free (tmp);
678 		      if (parent_id < 0)
679 			parent_id = 0;
680 		      g_free (filename);
681 		      filename = g_strdup (fields[i]);
682 		    }
683 		  else
684 		    {
685 		      directory = strcat (directory, fields[i]);
686 		      directory = strcat (directory, "/");
687 		    }
688 		}
689 	    }
690 	  DBG ("%s:%s:%d", filename, directory, parent_id);
691 
692 	  struct stat st;
693 	  uint64_t filesize;
694 	  fstat (fi->fh, &st);
695 	  filesize = (uint64_t) st.st_size;
696 
697 	  // Setup file
698 	  int ret;
699 	  LIBMTP_filetype_t filetype;
700 	  filetype = find_filetype (filename);
701 #ifdef USEMAD
702 	  if (filetype == LIBMTP_FILETYPE_MP3)
703 	    {
704 	      LIBMTP_track_t *genfile;
705 	      genfile = LIBMTP_new_track_t ();
706 	      gint songlen;
707 	      struct id3_file *id3_fh;
708 	      struct id3_tag *tag;
709 	      gchar *tracknum;
710 
711 	      id3_fh = id3_file_fdopen (fi->fh, ID3_FILE_MODE_READONLY);
712 	      tag = id3_file_tag (id3_fh);
713 
714 	      genfile->artist = getArtist (tag);
715 	      genfile->title = getTitle (tag);
716 	      genfile->album = getAlbum (tag);
717 	      genfile->genre = getGenre (tag);
718 	      genfile->date = getYear (tag);
719 	      genfile->usecount = 0;
720 	      genfile->parent_id = (uint32_t) parent_id;
721 	      genfile->storage_id = storageArea[storageid].storage->id;
722 
723 	      /* If there is a songlength tag it will take
724 	       * precedence over any length calculated from
725 	       * the bitrate and filesize */
726 	      songlen = getSonglen (tag);
727 	      if (songlen > 0)
728 		{
729 		  genfile->duration = songlen * 1000;
730 		}
731 	      else
732 		{
733 		  genfile->duration = (uint16_t) calc_length (fi->fh) * 1000;
734 		  //genfile->duration = 293000;
735 		}
736 
737 	      tracknum = getTracknum (tag);
738 	      if (tracknum != NULL)
739 		{
740 		  genfile->tracknumber = strtoul (tracknum, NULL, 10);
741 		}
742 	      else
743 		{
744 		  genfile->tracknumber = 0;
745 		}
746 	      g_free (tracknum);
747 
748 	      // Compensate for missing tag information
749 	      if (!genfile->artist)
750 		genfile->artist = g_strdup ("<Unknown>");
751 	      if (!genfile->title)
752 		genfile->title = g_strdup ("<Unknown>");
753 	      if (!genfile->album)
754 		genfile->album = g_strdup ("<Unknown>");
755 	      if (!genfile->genre)
756 		genfile->genre = g_strdup ("<Unknown>");
757 
758 	      genfile->filesize = filesize;
759 	      genfile->filetype = filetype;
760 	      genfile->filename = g_strdup (filename);
761 	      //title,artist,genre,album,date,tracknumber,duration,samplerate,nochannels,wavecodec,bitrate,bitratetype,rating,usecount
762 	      //DBG("%d:%d:%d",fi->fh,genfile->duration,genfile->filesize);
763 	      ret =
764 		LIBMTP_Send_Track_From_File_Descriptor
765 		(device, fi->fh, genfile, NULL, NULL);
766 	      id3_file_close (id3_fh);
767 	      LIBMTP_destroy_track_t (genfile);
768 	      DBG ("Sent TRACK %s", path);
769 	    }
770 	  else
771 	    {
772 #endif
773 	      LIBMTP_file_t *genfile;
774 	      genfile = LIBMTP_new_file_t ();
775 	      genfile->filesize = filesize;
776 	      genfile->filetype = filetype;
777 	      genfile->filename = g_strdup (filename);
778 	      genfile->parent_id = (uint32_t) parent_id;
779 	      genfile->storage_id = storageArea[storageid].storage->id;
780 
781 	      ret =
782 		LIBMTP_Send_File_From_File_Descriptor
783 		(device, fi->fh, genfile, NULL, NULL);
784 	      LIBMTP_destroy_file_t (genfile);
785 	      DBG ("Sent FILE %s", path);
786 #ifdef USEMAD
787 	    }
788 #endif
789 	  if (ret == 0)
790 	    {
791 	      DBG ("Sent %s", path);
792 	    }
793 	  else
794 	    {
795 	      DBG ("Problem sending %s - %d", path, ret);
796 	    }
797 	  // Cleanup
798 	  if (item && item->data)
799 	    g_free (item->data);
800 	  myfiles = g_slist_remove (myfiles, item->data);
801 	  g_strfreev (fields);
802 	  g_free (filename);
803 	  g_free (directory);
804 	  close (fi->fh);
805 	  // Refresh filelist
806 	  files_changed = TRUE;
807 	  return_unlock (ret);
808 	}
809     }
810   close (fi->fh);
811   return_unlock (0);
812 }
813 
814 void
mtpfs_destroy()815 mtpfs_destroy ()
816 {
817   enter_lock ("destroy");
818   if (files)
819     free_files (files);
820   int i;
821   for (i = 0; i < 4; i++)
822     {
823       if (storageArea[i].folders)
824 	LIBMTP_destroy_folder_t (storageArea[i].folders);
825     }
826   if (playlists)
827     free_playlists (playlists);
828   if (device)
829     LIBMTP_Release_Device (device);
830   return_unlock ();
831 }
832 
833 static int
mtpfs_readdir(const gchar * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)834 mtpfs_readdir (const gchar * path, void *buf, fuse_fill_dir_t filler,
835 	       off_t offset, struct fuse_file_info *fi)
836 {
837   enter_lock ("readdir %s", path);
838   LIBMTP_folder_t *folder;
839 
840   // Add common entries
841   filler (buf, ".", NULL, 0);
842   filler (buf, "..", NULL, 0);
843 
844   // If in root directory
845   if (strcmp (path, "/") == 0)
846     {
847       filler (buf, "Playlists", NULL, 0);
848       if (lostfiles != NULL)
849 	{
850 	  filler (buf, "lost+found", NULL, 0);
851 	}
852       LIBMTP_devicestorage_t *storage;
853       for (storage = device->storage; storage != 0; storage = storage->next)
854 	{
855 	  struct stat st;
856 	  memset (&st, 0, sizeof (st));
857 	  st.st_nlink = 2;
858 	  st.st_ino = storage->id;
859 	  st.st_mode = S_IFREG | 0555;
860 	  gchar *name;
861 	  filler (buf, storage->StorageDescription, &st, 0);
862 	}
863       return_unlock (0);
864     }
865   // Are we looking at the playlists?
866   if (strncmp (path, "/Playlists", 10) == 0)
867     {
868       DBG ("Checking Playlists");
869       LIBMTP_playlist_t *playlist;
870       check_playlists ();
871       playlist = playlists;
872       while (playlist != NULL)
873 	{
874 	  struct stat st;
875 	  memset (&st, 0, sizeof (st));
876 	  st.st_ino = playlist->playlist_id;
877 	  st.st_mode = S_IFREG | 0666;
878 	  gchar *name;
879 	  name = g_strconcat (playlist->name, ".m3u", NULL);
880 	  DBG ("Playlist:%s", name);
881 	  if (filler (buf, name, &st, 0))
882 	    {
883 	      g_free (name);
884 	      break;
885 	    }
886 	  g_free (name);
887 	  playlist = playlist->next;
888 	}
889       return_unlock (0);
890     }
891   // Are we looking at lost+found dir?
892   if (strncmp (path, "/lost+found", 11) == 0)
893     {
894       check_files ();
895       GSList *item;
896 
897       for (item = lostfiles; item != NULL; item = g_slist_next (item))
898 	{
899 	  LIBMTP_file_t *file = (LIBMTP_file_t *) item->data;
900 
901 	  struct stat st;
902 	  memset (&st, 0, sizeof (st));
903 	  st.st_ino = file->item_id;
904 	  st.st_mode = S_IFREG | 0444;
905 	  if (filler
906 	      (buf,
907 	       (file->filename ==
908 		NULL ? "<mtpfs null>" : file->filename), &st, 0))
909 	    break;
910 	}
911       return_unlock (0);
912     }
913   // Get storage area
914   int i;
915   int storageid = -1;
916   storageid = find_storage (path);
917   if (storageid < 0)
918     {
919       return_unlock (-ENOENT);
920     }
921   // Get folder listing.
922   int folder_id = 0;
923   if (strcmp (path, "/") != 0)
924     {
925       check_folders ();
926       folder_id =
927 	lookup_folder_id (storageArea[storageid].folders,
928 			  (gchar *) path, NULL);
929     }
930 
931   DBG ("Checking folders for %d", storageid);
932   check_folders ();
933   if (folder_id == -2)
934     {
935       DBG ("Root of storage area");
936       folder = storageArea[storageid].folders;
937     }
938   else
939     {
940       folder = LIBMTP_Find_Folder (storageArea[storageid].folders, folder_id);
941       if (folder == NULL)
942 	return_unlock (0);
943       folder = folder->child;
944     }
945   if (folder == NULL)
946     return_unlock (0);
947 
948   while (folder != NULL)
949     {
950       if ((folder->parent_id == folder_id) ||
951 	  (folder_id == -2
952 	   && (folder->storage_id == storageArea[storageid].storage->id)))
953 	{
954 	  DBG ("found folder: %s, id %d", folder->name, folder->folder_id);
955 	  struct stat st;
956 	  memset (&st, 0, sizeof (st));
957 	  st.st_ino = folder->folder_id;
958 	  st.st_mode = S_IFDIR | 0777;
959 	  if (filler (buf, folder->name, &st, 0))
960 	    break;
961 	}
962       folder = folder->sibling;
963     }
964   DBG ("Checking folders end");
965   LIBMTP_destroy_folder_t (folder);
966   DBG ("Checking files");
967   // Find files
968   LIBMTP_file_t *file, *tmp;
969   check_files ();
970   file = files;
971   while (file != NULL)
972     {
973       if ((file->parent_id == folder_id) ||
974 	  (folder_id == -2 && (file->parent_id == 0)
975 	   && (file->storage_id == storageArea[storageid].storage->id)))
976 	{
977 	  struct stat st;
978 	  memset (&st, 0, sizeof (st));
979 	  st.st_ino = file->item_id;
980 	  st.st_mode = S_IFREG | 0444;
981 	  if (filler
982 	      (buf,
983 	       (file->filename ==
984 		NULL ? "<mtpfs null>" : file->filename), &st, 0))
985 	    break;
986 	}
987       tmp = file;
988       file = file->next;
989     }
990   DBG ("readdir exit");
991   return_unlock (0);
992 }
993 
994 static int
mtpfs_getattr_real(const gchar * path,struct stat * stbuf)995 mtpfs_getattr_real (const gchar * path, struct stat *stbuf)
996 {
997   int ret = 0;
998   if (path == NULL)
999     return -ENOENT;
1000   memset (stbuf, 0, sizeof (struct stat));
1001 
1002   // Set uid/gid of file
1003   struct fuse_context *fc;
1004   fc = fuse_get_context ();
1005   stbuf->st_uid = fc->uid;
1006   stbuf->st_gid = fc->gid;
1007   if (strcmp (path, "/") == 0)
1008     {
1009       stbuf->st_mode = S_IFDIR | 0777;
1010       stbuf->st_nlink = 2;
1011       return 0;
1012     }
1013   // Check cached files first (stuff that hasn't been written to dev yet)
1014   GSList *item;
1015   if (myfiles != NULL)
1016     {
1017       item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
1018       if (item != NULL)
1019 	{
1020 	  stbuf->st_mode = S_IFREG | 0777;
1021 	  stbuf->st_size = 0;
1022 	  stbuf->st_blocks = 2;
1023 	  stbuf->st_mtime = time (NULL);
1024 	  return 0;
1025 	}
1026     }
1027   // Special case directory 'Playlists', 'lost+found'
1028   // Special case root directory items
1029   if (g_strrstr (path + 1, "/") == NULL)
1030     {
1031       stbuf->st_mode = S_IFDIR | 0777;
1032       stbuf->st_nlink = 2;
1033       return 0;
1034     }
1035 
1036   int storageid;
1037   storageid = find_storage (path);
1038   if (storageid < 0)
1039     {
1040       return -ENOENT;
1041     }
1042 
1043   if (g_ascii_strncasecmp (path, "/Playlists", 10) == 0)
1044     {
1045       LIBMTP_playlist_t *playlist;
1046       check_playlists ();
1047       playlist = playlists;
1048       while (playlist != NULL)
1049 	{
1050 	  gchar *tmppath;
1051 	  tmppath = g_strconcat ("/Playlists/", playlist->name, ".m3u", NULL);
1052 	  if (g_ascii_strcasecmp (path, tmppath) == 0)
1053 	    {
1054 	      int filesize = 0;
1055 	      int i;
1056 	      for (i = 0; i < playlist->no_tracks; i++)
1057 		{
1058 		  LIBMTP_file_t *file;
1059 		  LIBMTP_folder_t *folder;
1060 		  file =
1061 		    LIBMTP_Get_Filemetadata (device, playlist->tracks[i]);
1062 		  if (file != NULL)
1063 		    {
1064 		      int parent_id = file->parent_id;
1065 		      filesize = filesize + strlen (file->filename) + 2;
1066 		      while (parent_id != 0)
1067 			{
1068 			  check_folders ();
1069 			  folder =
1070 			    LIBMTP_Find_Folder
1071 			    (storageArea[storageid].folders, parent_id);
1072 			  if (folder == NULL)
1073 			    {
1074 			      DBG ("could not find %d in storage-area %d",
1075 				   parent_id, storageid);
1076 			      return -ENOENT;
1077 			    }
1078 			  parent_id = folder->parent_id;
1079 			  filesize = filesize + strlen (folder->name) + 1;
1080 			}
1081 		    }
1082 		}
1083 	      stbuf->st_mode = S_IFREG | 0777;
1084 	      stbuf->st_size = filesize;
1085 	      stbuf->st_blocks = 2;
1086 	      stbuf->st_mtime = time (NULL);
1087 	      return 0;
1088 	    }
1089 	  playlist = playlist->next;
1090 	}
1091       return -ENOENT;
1092     }
1093 
1094   if (strncasecmp (path, "/lost+found", 11) == 0)
1095     {
1096       GSList *item;
1097       int item_id = parse_path (path);
1098       for (item = lostfiles; item != NULL; item = g_slist_next (item))
1099 	{
1100 	  LIBMTP_file_t *file = (LIBMTP_file_t *) item->data;
1101 
1102 	  if (item_id == file->item_id)
1103 	    {
1104 	      stbuf->st_ino = item_id;
1105 	      stbuf->st_size = file->filesize;
1106 	      stbuf->st_blocks = (file->filesize / 512) +
1107 		(file->filesize % 512 > 0 ? 1 : 0);
1108 	      stbuf->st_nlink = 1;
1109 	      stbuf->st_mode = S_IFREG | 0777;
1110 	      stbuf->st_mtime = file->modificationdate;
1111 	      return 0;
1112 	    }
1113 	}
1114 
1115       return -ENOENT;
1116     }
1117 
1118   int item_id = -1;
1119   check_folders ();
1120   item_id =
1121     lookup_folder_id (storageArea[storageid].folders, (gchar *) path, NULL);
1122   if (item_id >= 0)
1123     {
1124       // Must be a folder
1125       stbuf->st_ino = item_id;
1126       stbuf->st_mode = S_IFDIR | 0777;
1127       stbuf->st_nlink = 2;
1128     }
1129   else
1130     {
1131       // Must be a file
1132       item_id = parse_path (path);
1133       LIBMTP_file_t *file;
1134       DBG ("id:path=%d:%s", item_id, path);
1135       check_files ();
1136       file = files;
1137       gboolean found = FALSE;
1138       while (file != NULL)
1139 	{
1140 	  if (file->item_id == item_id)
1141 	    {
1142 	      stbuf->st_ino = item_id;
1143 	      stbuf->st_size = file->filesize;
1144 	      stbuf->st_blocks = (file->filesize / 512) +
1145 		(file->filesize % 512 > 0 ? 1 : 0);
1146 	      stbuf->st_nlink = 1;
1147 	      stbuf->st_mode = S_IFREG | 0777;
1148 	      DBG ("time:%s", ctime (&(file->modificationdate)));
1149 	      stbuf->st_mtime = file->modificationdate;
1150 	      stbuf->st_ctime = file->modificationdate;
1151 	      stbuf->st_atime = file->modificationdate;
1152 	      found = TRUE;
1153 	    }
1154 	  file = file->next;
1155 	}
1156       if (!found)
1157 	{
1158 	  ret = -ENOENT;
1159 	}
1160     }
1161 
1162   return ret;
1163 }
1164 
1165 static int
mtpfs_getattr(const gchar * path,struct stat * stbuf)1166 mtpfs_getattr (const gchar * path, struct stat *stbuf)
1167 {
1168   enter_lock ("getattr %s", path);
1169 
1170   int ret = mtpfs_getattr_real (path, stbuf);
1171 
1172   DBG ("getattr exit");
1173   return_unlock (ret);
1174 }
1175 
1176 static int
mtpfs_mknod(const gchar * path,mode_t mode,dev_t dev)1177 mtpfs_mknod (const gchar * path, mode_t mode, dev_t dev)
1178 {
1179   enter_lock ("mknod %s", path);
1180   int item_id = parse_path (path);
1181   if (item_id > 0)
1182     return_unlock (-EEXIST);
1183   myfiles = g_slist_append (myfiles, (gpointer) (g_strdup (path)));
1184   DBG ("NEW FILE");
1185   return_unlock (0);
1186 }
1187 
1188 static int
mtpfs_open(const gchar * path,struct fuse_file_info * fi)1189 mtpfs_open (const gchar * path, struct fuse_file_info *fi)
1190 {
1191   enter_lock ("open");
1192   int item_id = -1;
1193   item_id = parse_path (path);
1194   if (item_id < 0)
1195     return_unlock (-ENOENT);
1196 
1197   switch (fi->flags & O_ACCMODE)
1198     {
1199     case O_RDONLY:
1200       DBG ("read");
1201       break;
1202     case O_WRONLY:
1203       DBG ("write");
1204       break;
1205     case O_RDWR:
1206       DBG ("rdwrite");
1207       break;
1208     default:
1209       DBG ("unexpected access mode: %d", fi->flags & O_ACCMODE);
1210     }
1211 
1212   int storageid;
1213   storageid = find_storage (path);
1214   if (storageid < 0)
1215     {
1216       return_unlock (-ENOENT);
1217     }
1218   FILE *filetmp = tmpfile ();
1219   int tmpfile = fileno (filetmp);
1220   if (tmpfile != -1)
1221     {
1222       if (item_id == 0)
1223 	{
1224 	  fi->fh = tmpfile;
1225 	}
1226       else if (strncmp ("/Playlists/", path, 11) == 0)
1227 	{
1228 	  // Is a playlist
1229 	  gchar **fields;
1230 	  fields = g_strsplit (path, "/", -1);
1231 	  gchar *name;
1232 	  name = g_strndup (fields[2], strlen (fields[2]) - 4);
1233 	  g_strfreev (fields);
1234 	  fi->fh = tmpfile;
1235 	  LIBMTP_playlist_t *playlist;
1236 	  check_playlists ();
1237 	  playlist = playlists;
1238 	  while (playlist != NULL)
1239 	    {
1240 	      if (g_ascii_strcasecmp (playlist->name, name) == 0)
1241 		{
1242 		  //int playlist_id=playlist->playlist_id;
1243 		  int i;
1244 		  for (i = 0; i < playlist->no_tracks; i++)
1245 		    {
1246 		      LIBMTP_file_t *file;
1247 		      LIBMTP_folder_t *folder;
1248 		      file =
1249 			LIBMTP_Get_Filemetadata (device, playlist->tracks[i]);
1250 		      if (file != NULL)
1251 			{
1252 			  gchar *path;
1253 			  path = (gchar *) g_malloc (1024);
1254 			  path = strcpy (path, "/");
1255 			  int parent_id = file->parent_id;
1256 			  while (parent_id != 0)
1257 			    {
1258 			      check_folders ();
1259 			      folder =
1260 				LIBMTP_Find_Folder
1261 				(storageArea[storageid].folders, parent_id);
1262 			      path = strcat (path, folder->name);
1263 			      path = strcat (path, "/");
1264 			      parent_id = folder->parent_id;
1265 			    }
1266 			  path = strcat (path, file->filename);
1267 			  fprintf (filetmp, "%s\n", path);
1268 			  DBG ("%s\n", path);
1269 			}
1270 		    }
1271 		  //LIBMTP_destroy_file_t(file);
1272 		  fflush (filetmp);
1273 		  break;
1274 		}
1275 	      playlist = playlist->next;
1276 	    }
1277 	}
1278       else
1279 	{
1280 	  int ret = LIBMTP_Get_File_To_File_Descriptor (device, item_id,
1281 							tmpfile,
1282 							NULL, NULL);
1283 	  if (ret == 0)
1284 	    {
1285 	      fi->fh = tmpfile;
1286 	    }
1287 	  else
1288 	    {
1289 	      return_unlock (-ENOENT);
1290 	    }
1291 	}
1292     }
1293   else
1294     {
1295       return_unlock (-ENOENT);
1296     }
1297 
1298   return_unlock (0);
1299 }
1300 
1301 static int
mtpfs_read(const gchar * path,gchar * buf,size_t size,off_t offset,struct fuse_file_info * fi)1302 mtpfs_read (const gchar * path, gchar * buf, size_t size, off_t offset,
1303 	    struct fuse_file_info *fi)
1304 {
1305   enter_lock ("read");
1306   int ret;
1307 
1308   int item_id = -1;
1309   item_id = parse_path (path);
1310   if (item_id < 0)
1311     return_unlock (-ENOENT);
1312 
1313   ret = pread (fi->fh, buf, size, offset);
1314   if (ret == -1)
1315     ret = -errno;
1316 
1317   return_unlock (ret);
1318 }
1319 
1320 static int
mtpfs_write(const gchar * path,const gchar * buf,size_t size,off_t offset,struct fuse_file_info * fi)1321 mtpfs_write (const gchar * path, const gchar * buf, size_t size, off_t offset,
1322 	     struct fuse_file_info *fi)
1323 {
1324   enter_lock ("write");
1325   int ret;
1326   if (fi->fh != -1)
1327     {
1328       ret = pwrite (fi->fh, buf, size, offset);
1329     }
1330   else
1331     {
1332       ret = -ENOENT;
1333     }
1334 
1335   return_unlock (ret);
1336 }
1337 
1338 static int
mtpfs_unlink(const gchar * path)1339 mtpfs_unlink (const gchar * path)
1340 {
1341   enter_lock ("unlink");
1342   int ret = 0;
1343   int item_id = -1;
1344   item_id = parse_path (path);
1345   if (item_id < 0)
1346     return_unlock (-ENOENT);
1347   ret = LIBMTP_Delete_Object (device, item_id);
1348   if (ret != 0)
1349     LIBMTP_Dump_Errorstack (device);
1350   if (strncmp (path, "/Playlists", 10) == 0)
1351     {
1352       playlists_changed = TRUE;
1353     }
1354   else
1355     {
1356       files_changed = TRUE;
1357     }
1358 
1359   return_unlock (ret);
1360 }
1361 
1362 static int
mtpfs_mkdir_real(const char * path,mode_t mode)1363 mtpfs_mkdir_real (const char *path, mode_t mode)
1364 {
1365   if (g_str_has_prefix (path, "/.Trash") == TRUE)
1366     return_unlock (-EPERM);
1367 
1368   int ret = 0;
1369   GSList *item;
1370   item = g_slist_find_custom (myfiles, path, (GCompareFunc) strcmp);
1371   int item_id = parse_path (path);
1372   int storageid = find_storage (path);
1373   if (storageid < 0)
1374     {
1375       return_unlock (-ENOENT);
1376     }
1377   if ((item == NULL) && (item_id < 0))
1378     {
1379       // Split path and find parent_id
1380       gchar *filename = g_strdup ("");
1381       gchar **fields;
1382       gchar *directory;
1383 
1384       directory = (gchar *) g_malloc (strlen (path) + 1);
1385       directory = strcpy (directory, "/");
1386       fields = g_strsplit (path, "/", -1);
1387       int i;
1388       uint32_t parent_id = 0;
1389       for (i = 0; fields[i] != NULL; i++)
1390 	{
1391 	  if (strlen (fields[i]) > 0)
1392 	    {
1393 	      if (fields[i + 1] == NULL)
1394 		{
1395 		  gchar *tmp = g_strndup (directory,
1396 					  strlen (directory) - 1);
1397 		  check_folders ();
1398 		  parent_id =
1399 		    lookup_folder_id (storageArea
1400 				      [storageid].folders, tmp, NULL);
1401 		  g_free (tmp);
1402 		  if (parent_id < 0)
1403 		    parent_id = 0;
1404 		  g_free (filename);
1405 		  filename = g_strdup (fields[i]);
1406 		}
1407 	      else
1408 		{
1409 		  directory = strcat (directory, fields[i]);
1410 		  directory = strcat (directory, "/");
1411 		}
1412 	    }
1413 	}
1414       DBG ("%s:%s:%d", filename, directory, parent_id);
1415       ret = LIBMTP_Create_Folder (device, filename, parent_id, 0);
1416       g_strfreev (fields);
1417       g_free (directory);
1418       g_free (filename);
1419       if (ret == 0)
1420 	{
1421 	  ret = -EEXIST;
1422 	}
1423       else
1424 	{
1425 	  storageArea[storageid].folders_changed = TRUE;
1426 	  ret = 0;
1427 	}
1428     }
1429   else
1430     {
1431       ret = -EEXIST;
1432     }
1433   return ret;
1434 }
1435 
1436 static int
mtpfs_mkdir(const char * path,mode_t mode)1437 mtpfs_mkdir (const char *path, mode_t mode)
1438 {
1439   enter_lock ("mkdir: %s", path);
1440   int ret = mtpfs_mkdir_real (path, mode);
1441 
1442   return_unlock (ret);
1443 }
1444 
1445 static int
mtpfs_rmdir(const char * path)1446 mtpfs_rmdir (const char *path)
1447 {
1448   enter_lock ("rmdir %s", path);
1449   int ret = 0;
1450   int folder_id = -1;
1451   if (strcmp (path, "/") == 0)
1452     {
1453       return_unlock (0);
1454     }
1455   int storageid = find_storage (path);
1456   if (storageid < 0)
1457     {
1458       return_unlock (-ENOENT);
1459     }
1460   folder_id =
1461     lookup_folder_id (storageArea[storageid].folders, (gchar *) path, NULL);
1462   if (folder_id < 0)
1463     return_unlock (-ENOENT);
1464 
1465   LIBMTP_Delete_Object (device, folder_id);
1466 
1467   storageArea[storageid].folders_changed = TRUE;
1468   return_unlock (ret);
1469 }
1470 
1471 /* Not working. need some way in libmtp to rename objects
1472 int
1473 mtpfs_rename (const char *oldname, const char *newname)
1474 {
1475     uint32_t old_id = parse_path(oldname);
1476     LIBMTP_track_t *track;
1477     track = LIBMTP_Get_Trackmetadata(device,old_id);
1478     gchar *filename;
1479     gchar **fields;
1480     gchar *directory;
1481     directory = (gchar *) g_malloc (strlen (newname) + 1);
1482     directory = strcpy (directory, "/");
1483     fields = g_strsplit (newname, "/", -1);
1484     int i;
1485     uint32_t parent_id = 0;
1486     for (i = 0; fields[i] != NULL; i++) {
1487         if (strlen (fields[i]) > 0) {
1488             if (fields[i + 1] == NULL) {
1489                 directory = g_strndup (directory, strlen (directory) - 1);
1490                 parent_id = lookup_folder_id (folders, directory, NULL);
1491                 if (parent_id < 0)
1492                     parent_id = 0;
1493                 filename = g_strdup (fields[i]);
1494             } else {
1495                 directory = strcat (directory, fields[i]);
1496                 directory = strcat (directory, "/");
1497 
1498             }
1499         }
1500     }
1501     DBG("%s:%s:%d", filename, directory, parent_id);
1502 
1503     track->parent_id = parent_id;
1504     track->title = g_strdup(filename);
1505     int ret = LIBMTP_Update_Track_Metadata(device, track);
1506     return ret;
1507 }
1508 */
1509 
1510 /* Allow renaming of empty folders only */
1511 int
mtpfs_rename(const char * oldname,const char * newname)1512 mtpfs_rename (const char *oldname, const char *newname)
1513 {
1514   enter_lock ("rename '%s' to '%s'", oldname, newname);
1515 
1516   int folder_id = -1, parent_id;
1517   int folder_empty = 1;
1518   int ret = -ENOTEMPTY;
1519   LIBMTP_folder_t *folder;
1520   LIBMTP_file_t *file;
1521 
1522   int storageid_old = find_storage (oldname);
1523   if (storageid_old < 0)
1524     {
1525       return_unlock (-ENOENT);
1526     }
1527   int storageid_new = find_storage (newname);
1528   if (storageid_new < 0)
1529     {
1530       return_unlock (-ENOENT);
1531     }
1532   if (strcmp (oldname, "/") != 0)
1533     {
1534       folder_id =
1535 	lookup_folder_id (storageArea[storageid_old].folders,
1536 			  (gchar *) oldname, NULL);
1537     }
1538   if (folder_id < 0)
1539     return_unlock (-ENOENT);
1540 
1541   check_folders ();
1542   folder = LIBMTP_Find_Folder (storageArea[storageid_old].folders, folder_id);
1543 
1544   /* MTP Folder object not found? */
1545   if (folder == NULL)
1546     return_unlock (-ENOENT);
1547 
1548   parent_id = folder->parent_id;
1549   folder = folder->child;
1550 
1551   /* Check if empty folder */
1552   DBG ("Checking empty folder start for: subfolders");
1553 
1554   while (folder != NULL)
1555     {
1556       if (folder->parent_id == folder_id)
1557 	{
1558 	  folder_empty = 0;
1559 	  break;
1560 	}
1561       folder = folder->sibling;
1562     }
1563 
1564   DBG ("Checking empty folder end for: subfolders. Result: %s",
1565        (folder_empty == 1 ? "empty" : "not empty"));
1566 
1567   if (folder_empty == 1)
1568     {
1569       /* Find files */
1570       check_files ();
1571       DBG ("Checking empty folder start for: files");
1572       file = files;
1573       while (file != NULL)
1574 	{
1575 	  if (file->parent_id == folder_id)
1576 	    {
1577 	      folder_empty = 0;
1578 	      break;
1579 	    }
1580 	  file = file->next;
1581 	}
1582       DBG ("Checking empty folder end for: files. Result: %s",
1583 	   (folder_empty == 1 ? "empty" : "not empty"));
1584 
1585       /* Rename folder. First remove old folder, then create the new one */
1586       if (folder_empty == 1)
1587 	{
1588 	  struct stat stbuf;
1589 	  if ((ret = mtpfs_getattr_real (oldname, &stbuf)) == 0)
1590 	    {
1591 	      DBG ("removing folder %s, id %d", oldname, folder_id);
1592 
1593 	      ret = mtpfs_mkdir_real (newname, stbuf.st_mode);
1594 	      LIBMTP_Delete_Object (device, folder_id);
1595 	      storageArea[storageid_old].folders_changed = TRUE;
1596 	      storageArea[storageid_new].folders_changed = TRUE;
1597 	    }
1598 	}
1599     }
1600   return_unlock (ret);
1601 }
1602 
1603 static int
mtpfs_statfs(const char * path,struct statfs * stbuf)1604 mtpfs_statfs (const char *path, struct statfs *stbuf)
1605 {
1606   DBG ("mtpfs_statfs");
1607   stbuf->f_bsize = 1024;
1608   stbuf->f_blocks = device->storage->MaxCapacity / 1024;
1609   stbuf->f_bfree = device->storage->FreeSpaceInBytes / 1024;
1610   stbuf->f_ffree = device->storage->FreeSpaceInObjects / 1024;
1611   stbuf->f_bavail = stbuf->f_bfree;
1612   return 0;
1613 }
1614 
1615 void *
mtpfs_init()1616 mtpfs_init ()
1617 {
1618   LIBMTP_devicestorage_t *storage;
1619   DBG ("mtpfs_init");
1620   files_changed = TRUE;
1621   playlists_changed = TRUE;
1622   DBG ("Ready");
1623   return 0;
1624 }
1625 
1626 int
mtpfs_blank()1627 mtpfs_blank ()
1628 {
1629   // Do nothing
1630 }
1631 
1632 static struct fuse_operations mtpfs_oper = {
1633   .chmod = mtpfs_blank,
1634   .release = mtpfs_release,
1635   .readdir = mtpfs_readdir,
1636   .getattr = mtpfs_getattr,
1637   .open = mtpfs_open,
1638   .mknod = mtpfs_mknod,
1639   .read = mtpfs_read,
1640   .write = mtpfs_write,
1641   .unlink = mtpfs_unlink,
1642   .destroy = mtpfs_destroy,
1643   .mkdir = mtpfs_mkdir,
1644   .rmdir = mtpfs_rmdir,
1645   .rename = mtpfs_rename,
1646   .statfs = mtpfs_statfs,
1647   .init = mtpfs_init,
1648 };
1649 
1650 int
main(int argc,char * argv[])1651 main (int argc, char *argv[])
1652 {
1653   int fuse_stat;
1654   umask (0);
1655   LIBMTP_raw_device_t *rawdevices;
1656   int numrawdevices;
1657   LIBMTP_error_number_t err;
1658   int device_number = 0;
1659 
1660   int opt;
1661   extern int optind;
1662   extern char *optarg;
1663 
1664   //while ((opt = getopt(argc, argv, "d")) != -1 ) {
1665   //switch (opt) {
1666   //case 'd':
1667   ////LIBMTP_Set_Debug(9);
1668   //break;
1669   //}
1670   //}
1671 
1672   //argc -= optind;
1673   //argv += optind;
1674 
1675   LIBMTP_Init ();
1676 
1677   fprintf (stdout, "Listing raw device(s)\n");
1678   err = LIBMTP_Detect_Raw_Devices (&rawdevices, &numrawdevices);
1679   switch (err)
1680     {
1681     case LIBMTP_ERROR_NO_DEVICE_ATTACHED:
1682       fprintf (stdout, "   No raw devices found.\n");
1683       return 0;
1684     case LIBMTP_ERROR_CONNECTING:
1685       fprintf (stderr,
1686 	       "Detect: There has been an error connecting. Exiting\n");
1687       return 1;
1688     case LIBMTP_ERROR_MEMORY_ALLOCATION:
1689       fprintf (stderr,
1690 	       "Detect: Encountered a Memory Allocation Error. Exiting\n");
1691       return 1;
1692     case LIBMTP_ERROR_NONE:
1693       {
1694 	int i;
1695 
1696 	fprintf (stdout, "   Found %d device(s):\n", numrawdevices);
1697 	for (i = 0; i < numrawdevices; i++)
1698 	  {
1699 	    if (rawdevices[i].device_entry.vendor != NULL ||
1700 		rawdevices[i].device_entry.product != NULL)
1701 	      {
1702 		fprintf (stdout,
1703 			 "   %s: %s (%04x:%04x) @ bus %d, dev %d\n",
1704 			 rawdevices[i].device_entry.vendor,
1705 			 rawdevices[i].device_entry.product,
1706 			 rawdevices[i].device_entry.vendor_id,
1707 			 rawdevices[i].device_entry.product_id,
1708 			 rawdevices[i].bus_location, rawdevices[i].devnum);
1709 	      }
1710 	    else
1711 	      {
1712 		fprintf (stdout,
1713 			 "   %04x:%04x @ bus %d, dev %d\n",
1714 			 rawdevices[i].device_entry.vendor_id,
1715 			 rawdevices[i].device_entry.product_id,
1716 			 rawdevices[i].bus_location, rawdevices[i].devnum);
1717 	      }
1718 	  }
1719       }
1720       break;
1721     case LIBMTP_ERROR_GENERAL:
1722     default:
1723       fprintf (stderr, "Unknown connection error.\n");
1724       return 1;
1725     }
1726 
1727   fprintf (stdout, "Attempting to connect device\n");
1728   device = LIBMTP_Open_Raw_Device (&rawdevices[device_number]);
1729   if (device == NULL)
1730     {
1731       fprintf (stderr, "Unable to open raw device %d\n", device_number);
1732       return 1;
1733     }
1734 
1735   LIBMTP_Dump_Errorstack (device);
1736   LIBMTP_Clear_Errorstack (device);
1737 
1738   char *friendlyname;
1739   /* Echo the friendly name so we know which device we are working with */
1740   friendlyname = LIBMTP_Get_Friendlyname (device);
1741   if (friendlyname == NULL)
1742     {
1743       printf ("Listing File Information on Device with name: (NULL)\n");
1744     }
1745   else
1746     {
1747       printf ("Listing File Information on Device with name: %s\n",
1748 	      friendlyname);
1749       g_free (friendlyname);
1750     }
1751 
1752   /* Get all storages for this device */
1753   int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
1754   if (ret != 0)
1755     {
1756       fprintf (stdout, "LIBMTP_Get_Storage() failed:%d\n", ret);
1757       LIBMTP_Dump_Errorstack (device);
1758       LIBMTP_Clear_Errorstack (device);
1759       return 1;
1760     }
1761 
1762   /* Check if multiple storage areas */
1763   LIBMTP_devicestorage_t *storage;
1764   int i;
1765   for (storage = device->storage, i = 0; storage != 0;
1766        storage = storage->next, i++)
1767     {
1768       storageArea[i].storage = storage;
1769       storageArea[i].folders = NULL;
1770       storageArea[i].folders_changed = TRUE;
1771       DBG ("Storage%d: %d - %s\n", i, storage->id,
1772 	   storage->StorageDescription);
1773     }
1774 
1775   DBG ("Start fuse");
1776 
1777   fuse_stat = fuse_main (argc, argv, &mtpfs_oper, NULL);
1778   DBG ("fuse_main returned %d\n", fuse_stat);
1779   return fuse_stat;
1780 }
1781 
1782 #ifdef USEMAD
1783 /* Private buffer for passing around with libmad */
1784 typedef struct
1785 {
1786   /* The buffer of raw mpeg data for libmad to decode */
1787   void *buf;
1788 
1789   /* Cached data: pointers to the dividing points of frames
1790      in buf, and the playing time at each of those frames */
1791   void **frames;
1792   mad_timer_t *times;
1793 
1794   /* fd is the file descriptor if over the network, or -1 if
1795      using mmap()ed files */
1796   int fd;
1797 
1798   /* length of the current stream, corrected for id3 tags */
1799   ssize_t length;
1800 
1801   /* have we finished fetching this file? (only in non-mmap()'ed case */
1802   int done;
1803 
1804   /* total number of frames */
1805   unsigned long num_frames;
1806 
1807   /* number of frames to play */
1808   unsigned long max_frames;
1809 
1810   /* total duration of the file */
1811   mad_timer_t duration;
1812 
1813   /* filename as mpg321 has opened it */
1814   char filename[PATH_MAX];
1815 } buffer;
1816 
1817 /* XING parsing is from the MAD winamp input plugin */
1818 
1819 struct xing
1820 {
1821   int flags;
1822   unsigned long frames;
1823   unsigned long bytes;
1824   unsigned char toc[100];
1825   long scale;
1826 };
1827 
1828 enum
1829 {
1830   XING_FRAMES = 0x0001,
1831   XING_BYTES = 0x0002,
1832   XING_TOC = 0x0004,
1833   XING_SCALE = 0x0008
1834 };
1835 
1836 #define XING_MAGIC     (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
1837 
1838 /* Following two function are adapted from mad_timer, from the
1839    libmad distribution */
1840 static int
parse_xing(struct xing * xing,struct mad_bitptr ptr,unsigned int bitlen)1841 parse_xing (struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
1842 {
1843   if (bitlen < 64 || mad_bit_read (&ptr, 32) != XING_MAGIC)
1844     goto fail;
1845 
1846   xing->flags = mad_bit_read (&ptr, 32);
1847   bitlen -= 64;
1848 
1849   if (xing->flags & XING_FRAMES)
1850     {
1851       if (bitlen < 32)
1852 	goto fail;
1853 
1854       xing->frames = mad_bit_read (&ptr, 32);
1855       bitlen -= 32;
1856     }
1857 
1858   if (xing->flags & XING_BYTES)
1859     {
1860       if (bitlen < 32)
1861 	goto fail;
1862 
1863       xing->bytes = mad_bit_read (&ptr, 32);
1864       bitlen -= 32;
1865     }
1866 
1867   if (xing->flags & XING_TOC)
1868     {
1869       int i;
1870 
1871       if (bitlen < 800)
1872 	goto fail;
1873 
1874       for (i = 0; i < 100; ++i)
1875 	xing->toc[i] = mad_bit_read (&ptr, 8);
1876 
1877       bitlen -= 800;
1878     }
1879 
1880   if (xing->flags & XING_SCALE)
1881     {
1882       if (bitlen < 32)
1883 	goto fail;
1884 
1885       xing->scale = mad_bit_read (&ptr, 32);
1886       bitlen -= 32;
1887     }
1888 
1889   return 1;
1890 fail:
1891   xing->flags = 0;
1892   return 0;
1893 }
1894 
1895 int
scan(void const * ptr,ssize_t len)1896 scan (void const *ptr, ssize_t len)
1897 {
1898   int duration = 0;
1899   struct mad_stream stream;
1900   struct mad_header header;
1901   struct xing xing;
1902   xing.frames = 0;
1903 
1904   unsigned long bitrate = 0;
1905   int has_xing = 0;
1906   int is_vbr = 0;
1907   mad_stream_init (&stream);
1908   mad_header_init (&header);
1909 
1910   mad_stream_buffer (&stream, ptr, len);
1911 
1912   int num_frames = 0;
1913 
1914   /* There are three ways of calculating the length of an mp3:
1915      1) Constant bitrate: One frame can provide the information
1916      needed: # of frames and duration. Just see how long it
1917      is and do the division.
1918      2) Variable bitrate: Xing tag. It provides the number of
1919      frames. Each frame has the same number of samples, so
1920      just use that.
1921      3) All: Count up the frames and duration of each frames
1922      by decoding each one. We do this if we've no other
1923      choice, i.e. if it's a VBR file with no Xing tag.
1924    */
1925 
1926   while (1)
1927     {
1928       if (mad_header_decode (&header, &stream) == -1)
1929 	{
1930 	  if (MAD_RECOVERABLE (stream.error))
1931 	    continue;
1932 	  else
1933 	    break;
1934 	}
1935 
1936       /* Limit xing testing to the first frame header */
1937       if (!num_frames++)
1938 	{
1939 	  if (parse_xing (&xing, stream.anc_ptr, stream.anc_bitlen))
1940 	    {
1941 	      is_vbr = 1;
1942 
1943 	      if (xing.flags & XING_FRAMES)
1944 		{
1945 		  /* We use the Xing tag only for frames. If it doesn't have that
1946 		     information, it's useless to us and we have to treat it as a
1947 		     normal VBR file */
1948 		  has_xing = 1;
1949 		  num_frames = xing.frames;
1950 		  break;
1951 		}
1952 	    }
1953 	}
1954 
1955       /* Test the first n frames to see if this is a VBR file */
1956       if (!is_vbr && !(num_frames > 20))
1957 	{
1958 	  if (bitrate && header.bitrate != bitrate)
1959 	    {
1960 	      is_vbr = 1;
1961 	    }
1962 
1963 	  else
1964 	    {
1965 	      bitrate = header.bitrate;
1966 	    }
1967 	}
1968 
1969       /* We have to assume it's not a VBR file if it hasn't already been
1970          marked as one and we've checked n frames for different bitrates */
1971       else if (!is_vbr)
1972 	{
1973 	  break;
1974 	}
1975 
1976       duration = header.duration.seconds;
1977     }
1978 
1979   if (!is_vbr)
1980     {
1981       double time = (len * 8.0) / (header.bitrate);	/* time in seconds */
1982       //double timefrac = (double)time - ((long)(time));
1983       long nsamples = 32 * MAD_NSBSAMPLES (&header);	/* samples per frame */
1984 
1985       /* samplerate is a constant */
1986       num_frames = (long) (time * header.samplerate / nsamples);
1987 
1988       duration = (int) time;
1989       DBG ("d:%d", duration);
1990     }
1991 
1992   else if (has_xing)
1993     {
1994       /* modify header.duration since we don't need it anymore */
1995       mad_timer_multiply (&header.duration, num_frames);
1996       duration = header.duration.seconds;
1997     }
1998 
1999   else
2000     {
2001       /* the durations have been added up, and the number of frames
2002          counted. We do nothing here. */
2003     }
2004 
2005   mad_header_finish (&header);
2006   mad_stream_finish (&stream);
2007   return duration;
2008 }
2009 
2010 int
calc_length(int f)2011 calc_length (int f)
2012 {
2013   struct stat filestat;
2014   void *fdm;
2015   char buffer[3];
2016 
2017   fstat (f, &filestat);
2018 
2019   /* TAG checking is adapted from XMMS */
2020   int length = filestat.st_size;
2021 
2022   if (lseek (f, -128, SEEK_END) < 0)
2023     {
2024       /* File must be very short or empty. Forget it. */
2025       return -1;
2026     }
2027 
2028   if (read (f, buffer, 3) != 3)
2029     {
2030       return -1;
2031     }
2032 
2033   if (!strncmp (buffer, "TAG", 3))
2034     {
2035       length -= 128;		/* Correct for id3 tags */
2036     }
2037 
2038   fdm = mmap (0, length, PROT_READ, MAP_SHARED, f, 0);
2039   if (fdm == MAP_FAILED)
2040     {
2041       g_error ("Map failed");
2042       return -1;
2043     }
2044 
2045   /* Scan the file for a XING header, or calculate the length,
2046      or just scan the whole file and add everything up. */
2047   int duration = scan (fdm, length);
2048 
2049   if (munmap (fdm, length) == -1)
2050     {
2051       g_error ("Unmap failed");
2052       return -1;
2053     }
2054 
2055   lseek (f, 0, SEEK_SET);
2056   return duration;
2057 }
2058 
2059 #endif
2060