Highlighted

ExtendScript Oddity with File/Folder on Mac OS X

Contributor ,
Feb 05, 2012

Copy link to clipboard

Copied

Here's a really odd one, happens on Mac OS X Lion 10.7.3 with InDesign CS5.5.

Enter the following little InDesign Script in ExtendScript Toolkit, and run it targeting InDesign CS5.5:

// Create a path of the form /Users/kris/Desktop or something similar, then resolve the path back to a File object

var fn = File(File("~").fsName + "/Desktop");

alert(fn.fsName + " exists: " + fn.exists);

If all is well, you get a dialog saying something like "/Users/kris/Desktop exists: true".

Nothing weird yet. Leave ExtendScript Toolkit running for a sec.

Now start up a Terminal window, and go to the /Volumes folder. Create a subfolder called Users (so  that the folder /Volumes/Users exists on your computer).

Re-run the script.

Weirdness: I get "/Volumes/Users/kris/Desktop exists: false". Euh?

Anyone seen that before? Don't forget to remove "/Volumes/Users" again!

It is probably related to another weirdness. Run this one-liner:

alert(File("///").fsName);

You'll get "/Volumes" - but you'd expect to get "/", no?

Topics

Scripting

Views

13.8K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

ExtendScript Oddity with File/Folder on Mac OS X

Contributor ,
Feb 05, 2012

Copy link to clipboard

Copied

Here's a really odd one, happens on Mac OS X Lion 10.7.3 with InDesign CS5.5.

Enter the following little InDesign Script in ExtendScript Toolkit, and run it targeting InDesign CS5.5:

// Create a path of the form /Users/kris/Desktop or something similar, then resolve the path back to a File object

var fn = File(File("~").fsName + "/Desktop");

alert(fn.fsName + " exists: " + fn.exists);

If all is well, you get a dialog saying something like "/Users/kris/Desktop exists: true".

Nothing weird yet. Leave ExtendScript Toolkit running for a sec.

Now start up a Terminal window, and go to the /Volumes folder. Create a subfolder called Users (so  that the folder /Volumes/Users exists on your computer).

Re-run the script.

Weirdness: I get "/Volumes/Users/kris/Desktop exists: false". Euh?

Anyone seen that before? Don't forget to remove "/Volumes/Users" again!

It is probably related to another weirdness. Run this one-liner:

alert(File("///").fsName);

You'll get "/Volumes" - but you'd expect to get "/", no?

Topics

Scripting

Views

13.8K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

Weird stuff! It happens on Snow Leopard as well...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Most Valuable Participant ,
Feb 06, 2012

Copy link to clipboard

Copied

Weird.

Is this just idle experimentation?  The Folder Class Folder.desktop (http://jongware.mit.edu/idcs5/pc_Folder.html) points directly to your desktop -- Windows compatible as well (I'm not sure your "~" notation is).

I don't know much about the low-level organization of Unix-style disk mountings, but isn't "/Volumes" more of a virtual construct by the OS than a physical one? Not at work today else I'd try (Dutch railroad made me wait for more than an hour last week 'cause of 1 inch of snow -- huh! "Extreme circumstances"! -- and now I have a severe headcold), but I think using plain "/" on a Windows set would bring you just to the root of the current drive, and I don't think there is a way to get a list of "mounted volumes" under Windows. The Mac solution, providing a higher-than-root-dir entry called Volumes solves this.

As for your "Users" question, well, you might be pushing against the boundaries of what's wise 😉 Folder names such as 'users' (lowercase) have special meanings in any OS, and trying to fiddle with Case Variations ("uSers", "deskTOP") may not only confuse ExtendScript -- which, remember, tries to mediate between Windows and Mac system folders -- but utlimately may bring down your Mac as well.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Contributor ,
Feb 06, 2012

Copy link to clipboard

Copied

Hi Jongware!

Ignore the "~" stuff or the fact that I am using "/Users" to demonstrate it - it's my way to get the issue demonstrated in two lines.

I just tested some more: if I create any folder in the hard disk's root (e.g. /MyFolder) and put some file (e.g. test.txt) into it, there is no problem. Then, when I create the totally unrelated folder /Volumes/MyFolder, I suddenly get strange behavior, because for some odd reason, the ExtendScript path resolver prefers "/Volumes/MyFolder" instead of "/MyFolder" as a match to the expression "/MyFolder."

The thing is that "/whateveryouwant" gets resolved incorrectly on Mac OS X whenever there is a /Volumes/whateveryouwant folder. It gets resolved correctly if there is no /Volumes/whateveryouwant folder.

In other words, a constant string path gets interpreted differently dependent on the existence or non-existence of a seemingly unrelated folder. Internally, the algorithm that resolves a string path into a File or Folder object is using some incorrect logic, IMHO.

To try it out: create directory "/MyFolder", create a file "/MyFolder/text.txt" (keep in mind, we're not discussing whether that is wise or not - that's another discussion).

Now run

f = File("/MyFolder/test.txt");

alert(f.exists);

You get 'true'. Ok

Now create the directory "/Volumes/MyFolder" and re-run the same script

You get 'false'.

Now, before you say "Don't create any folders in /Volumes, then!" - the problem is that our users do this all the time! E.g. they have a USB drive called 'Users'. They insert it - KABOOM! Suddenly our scripts lose access to /Users/... because inserting that USB drive created the mount point /Volumes/Users. So, no - it's not idle experimentation. It's blood, sweat and tears 😞

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

Yeah. This seems to me a pretty major bug in ExtendScript (it's not limited to InDesign).

I'd log this bugger, and bug someone at Adobe to get this fixed. I wish I knew who to bug about that...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Adobe Community Professional ,
Feb 06, 2012

Copy link to clipboard

Copied

There's a whole section about this in the Guide. Seems vaguely relevant...

Volume and drive names

A volume or drive name can be the first part of an absolute path in URI

notation. The values are interpreted

according to the platform.

Mac OS volumes

When Mac OS X starts, the startup volume is the root directory of the

file system. All other volumes,

including remote volumes, are part of the /Volumes directory. The File

and Folder objects use these

rules to interpret the first element of a path name:

➤ If the name is the name of the startup volume, discard it.

➤ If the name is a volume name, prepend /Volumes.

➤ Otherwise, leave the path as is.

Ariel

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

Interesting. Faulty logic if you ask me...

This seems to be a workable workaround assuming there's a good way of discovering the root volume name: (any suggestions on that front?)

var rootName = "Gabe";

var fn = File(File("~").fsName + "/Desktop");

alert(fn.fsName + " exists: " + fn.exists);

fn = File(fn.fsName.replace(/^\/Volumes/,"/" + rootName));

alert(fn.fsName + " exists: " + fn.exists);

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Enthusiast ,
Feb 06, 2012

Copy link to clipboard

Copied

...assuming there's a good way of discovering the root volume name: (any suggestions on that front?)

var rootName = app.doScript("tell application \"Finder\" to return name of startup disk", ScriptLanguage.APPLESCRIPT_LANGUAGE);

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

Thanks Jeff.

Yes. That'll work nicely for InDesign. Of course only InDesign has doScript(). I suppose system() will work for the apps that support that.

I wonder if there's an environment variable which stores the startup disk name...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Contributor ,
Feb 06, 2012

Copy link to clipboard

Copied

var f = File("///").parent;

alert(f.displayName);

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

Very nice!

So I guess the "solution" is something like this:

var fn = normalizeFilePath(File(File("~").fsName + "/Desktop"));

function normalizeFilePath(file){

          if(File.fs != "Macintosh"){return file}

          return File(file.fsName.replace(/^\/Volumes/,"/" + File("///").parent.displayName));

}

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Contributor ,
Feb 06, 2012

Copy link to clipboard

Copied

Building on Harb's solution: a wrapper for 'new File'. Haven't tested it extensively yet, and I am not sure it's covering all the bases, so take it for what it's worth:

function newFile(filePath)

{

          do

          {

                    try

                    {

                              var retVal = new File(filePath);

                              if (File.fs != "Macintosh")

                              {

                                        break;

                              }

 

                              if (retVal.fsName == filePath)

                              {

                                        break;

                              }

 

                              var rootVolumeName = File("/").parent.displayName;

                              retVal = new File(retVal.fsName.replace(/^\/Volumes\//,"/" + rootVolumeName + "/"));

                    }

                    catch (error)

                    {

                              retVal = null;

                    }

          }

          while (false);

 

          return retVal;

}



Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Contributor ,
Feb 06, 2012

Copy link to clipboard

Copied

Funny. Nearly identical, eh!

I tried to make sure that any 'legal' accesses to /Volumes are also covered (whether ill-advised or not).

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

Looks pretty good to me. The only way I can see breaking this, is if there's more bugs that'll change the file path that we don't know about...

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

RorohikoKris wrote:

I tried to make sure that any 'legal' accesses to /Volumes are also covered (whether ill-advised or not).

Yeah. I noticed that. Nice touch.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Most Valuable Participant ,
Feb 06, 2012

Copy link to clipboard

Copied

I'm still a bit groggy due to aformentioned PT shenanigans (now they're discussing reduced services the entire week, can you believe it!) but is this related to last month's thread on the Illustrator forum?

"Error on opening file after upgrade to Lion" -- http://forums.adobe.com/thread/938672?tstart=0

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 06, 2012

Copy link to clipboard

Copied

[Jongware] wrote:

...but is this related to last month's thread on the Illustrator forum?

Nope. This is not Lion specific, and it has nothing to do with a file prefix. Nothing to do with openDialog() either...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
New Here ,
Mar 03, 2015

Copy link to clipboard

Copied

Thanks for pointing out at File and Folder handle paths differently so you can get the username from File.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Community Beginner ,
Feb 09, 2012

Copy link to clipboard

Copied

It seems the Folder.create() function is immune to this hack.

I can successfully reference a folder object on my desktop using the hack function:

          var folder = Folder(newFile("~/Desktop/Test"));

But if you still have a Users folder in your Volumes, calling:

          folder.create();

Will return true but won't create the folder on the desktop.

Where was it created? In the /Volumes/Users directory!

=> /Volumes/Users/[USERNAME]/Desktop/Test

Joris

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 09, 2012

Copy link to clipboard

Copied

Hi Joris,

Ugh! I just tried using changePath() to try to move the folder to the correct location afterwards, but it does not seem to work as I'd hoped...

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 09, 2012

Copy link to clipboard

Copied

I guess using AppleScript to create the folder would be a workaround, but all this stuff is pretty bad...

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Contributor ,
Feb 09, 2012

Copy link to clipboard

Copied

Try this wrapper for new Folder(...):

function newFolder(filePath)

{

    var retVal;

    do

    {

        try

        {

            if (File.fs != "Macintosh")

            {

                break;

            }

            if (filePath.match(/^\/Volumes\//i) != null)

            {

                break;

            }

            if (filePath.charAt(0) == "~")

            {

                filePath = File("~").fsName + filePath.substr(1);

            }

            else if (filePath.charAt(0) != "/")

            {

                break;

            }

            var rootVolumeName = File("/").parent.displayName;

            retVal = new Folder("/Volumes/" + rootVolumeName + "/" + filePath);

        }

        catch (error)

        {

        }

    }

    while (false);

    if (retVal == null)

    {

        retVal = new Folder(filePath);

    }

    return retVal;

}

If /Volumes/Users exists on my system so it interferes with the ExtendScript file handling, then:

var folder = new Folder("~/Desktop/Test");

folder.create();

fails, but

var folder = newFolder("~/Desktop/Test");

folder.create();

succeeds

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Community Beginner ,
Feb 14, 2012

Copy link to clipboard

Copied

That works!

I modified both functions a little bit to work for the following test cases:

  • NewFolder("~/Desktop/TestFolder");
  • NewFolder("/Users/[USERNAME]/Desktop/TestFolder");
  • NewFolder("/Volumes/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFolder");
  • NewFolder("/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFolder");

  • NewFile("~/Desktop/TestFile.txt");
  • NewFile("/Users/[USERNAME]/Desktop/TestFile.txt");
  • NewFile("/Volumes/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFile.txt");
  • NewFile("/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFile.txt");

They also work with legal accesses, (if you actually wanted to create something at /Volumes/Users)

  i.e:

  • NewFolder("/Volumes/Users/TestFolder");
  • NewFile("/Volumes/Users/TestFile.txt");

You can also use them correctly in combination with File/Folder objects:

  i.e:

  var folder = NewFolder("~/Desktop/TestFolder");

  var sameFolder = NewFolder(folder);

  var folder = NewFolder("~/Desktop/TestFolder");

  var file = NewFile(folder + "/TestFile.txt");

Functions:

function NewFile(in_path)

{

 

  var file;

 

  try

  {

    do

    {

     

      if (in_path instanceof File)

        var path = in_path.absoluteURI;

      else if (in_path instanceof Folder)

        var path = in_path.absoluteURI;

      else

        var path = in_path;

     

      file = new File(path)

     

      if (File.fs != "Macintosh")

        break;

     

      var filePath = file.fsName;

      if (filePath == path) // File path wasn't changed by Extendscript

        break;

     

      if (filePath.match(/^\/Volumes\//i) == null) // File path isn't a Volumes path

        break;

     

      var rootVolumeName = File("/").parent.displayName;

      filePath = filePath.replace(/^\/Volumes\//,"/" + rootVolumeName + "/")

     

      file = new File(filePath);

     

    }

    while (false);

  }

  catch (err)

  {

    file = null;

  }

 

  return file;

 

}

// ******************************

function NewFolder(in_path)

{

 

  var folder;

 

  try

  {

    do

    {

     

     

      if (in_path instanceof File)

        var path = in_path.absoluteURI;

      else if (in_path instanceof Folder)

        var path = in_path.absoluteURI;

      else

        var path = in_path;

     

      folder = new Folder(path)

     

      if (File.fs != "Macintosh")

        break;

     

      var folderPath = folder.fsName;

      if (folderPath == path) // Folder path wasn't changed by Extendscript

        break;

     

      if (path.charAt(0) == "~")

        path = File("~").fsName + path.substr(1);

     

      if (path.charAt(0) != "/") // Not an absolute path

        break;

     

      var rootVolumeName = File("/").parent.displayName;

      folder = new Folder("/Volumes/" + rootVolumeName + path);

     

    }

    while (false);

  }

  catch (err)

  {

    folder = null;

  }

 

  return folder;

 

}

Joris

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
LEGEND ,
Feb 15, 2012

Copy link to clipboard

Copied

Great stuff!

Harbs

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Explorer ,
Jun 12, 2020

Copy link to clipboard

Copied

8 years later and I encounter the same problem. Thank you Kris and Joris!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
New Here ,
Jun 03, 2016

Copy link to clipboard

Copied

In a really weird twist... this article helped me in my situation!

I was getting a "Directory does not exist: /Volumes/Users/mac/desktop/render/" error every time I used terminal renderer. The problem wound up being that I have a server mount which is also called "Users" and houses user folders for folks in the office. Presumably, the path to my render folder set in AE would work fine, but in terminal presented with two /Volumes/Users paths, it chose the wrong one and errored out looking for the rest of the path. Once I unmounted this server, the terminal renderer completed with no errors. Adobe guys, there seems to be a glitch with how paths are read here, no?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Community Beginner ,
Feb 14, 2020

Copy link to clipboard

Copied

Ok this is the 2020 fix for this problem on OSX. 

 

So as discussed in this thread, the problem is if you have a USB or network drive Mounted with a Users folder (which will show in the terminal under /Volumes/Users/) then try to open the file  File('/Users/bob/'), extend script will instead open the mounted folder first "/Volumes/Users/bob" and fail.

 

The other solutions in this post to get the OSX hard drive name does not work in the latest CC2020 and OSX Catalina, the old solution of File("///").parent.displayName now returned a blank string. Ugh, You can manually use File('/Mackintosh HD/Users/bob/') if you know the hard drive is called 'Macintosh HD' but on other computers, this might fail.

 

I spent half a day trying a lot of different things and found a simple fix. add "/../" before the path

 

using var file = File('/../Users/bob/')  works because it will tranverse back a folder

 

If you check the file object path alert(file.fsName) it will return '/Volumes/../Users/bob/' Extend script still adds the Volumes folder but then /../ transverses back to the root /Users folder

 

in fact add in as bay as you like  File('/../../../Users/bob/') also works, as the parent '..' of the root is the root.

 

Hope this helps anyone in the future who is driven mad by this problem.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Explorer ,
Jun 12, 2020

Copy link to clipboard

Copied

Thank you, does it support cases, that @Joris_Coppieters wrapper works with?

  • NewFolder("~/Desktop/TestFolder");
  • NewFolder("/Users/[USERNAME]/Desktop/TestFolder");
  • NewFolder("/Volumes/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFolder");
  • NewFolder("/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFolder");
  • NewFile("~/Desktop/TestFile.txt");
  • NewFile("/Users/[USERNAME]/Desktop/TestFile.txt");
  • NewFile("/Volumes/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFile.txt");
  • NewFile("/[ROOTVOLUME]/Users/[USERNAME]/Desktop/TestFile.txt");

 

I've tested and if I want to get file from actual "Volume/DriveName/Folder" I will get "Volume/../Volume/DriveName/Folder" which will be unusable.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Advisor ,
Jun 14, 2020

Copy link to clipboard

Copied

Here some AppleScripts that may be useful:

 

app.doScript('tell application "System Events" to get name of startup disk',ScriptLanguage.APPLESCRIPT_LANGUAGE)
app.doScript('get POSIX path of (path to home folder)',ScriptLanguage.APPLESCRIPT_LANGUAGE)
app.doScript('get POSIX path of (path to preferences folder)',ScriptLanguage.APPLESCRIPT_LANGUAGE)
app.doScript('get POSIX path of (path to library folder from user domain)',ScriptLanguage.APPLESCRIPT_LANGUAGE)
app.doScript('get POSIX path of (path to desktop folder)',ScriptLanguage.APPLESCRIPT_LANGUAGE)

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Explorer ,
Jun 14, 2020

Copy link to clipboard

Copied

Thank you, but as far as I know that will work for InDesign only.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Advisor ,
Jun 15, 2020

Copy link to clipboard

Copied

I used File.execute() from scripts running within the ESTK, but avoiding terminal.app windows requires a complicated construction with an AppleScript helper app, also temporary files for the script and output.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Explorer ,
Jun 15, 2020

Copy link to clipboard

Copied

It is still a solution! I actually never used File.execute() command, will remember it, one day might be usefull.
found this way to hide shell script https://superuser.com/a/1354541

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...