Archive for the ‘Code’ Category

Customizing the suggested filename in Save dialogs

Thursday, July 25th, 2024

I got a request from a Default Folder X user yesterday, asking if I could add a feature to limit the length of the default filename supplied in Save As dialogs. Apparently, the cloud services and backup software that he and his colleagues use can’t handle filenames longer than 45 characters, so he wants to make sure they don’t save files with names longer than that. That’s a pretty unfortunate limitation of the software they’re using (I mean really, in 2024?), but I’m all for avoiding stupid problems.

I’ve addressed a similar request before, automatically prepending dates to the names of files as you save them. This post is an update, because Default Folder X now has a better way to handle this than the method described in that earlier post.

Background

Default Folder X has a flexible, AppleScript-based method for handling customized default folders and filenames in Save dialogs. Yeah, I know AppleScript is old and crufty, but it’s available on every Mac, can interoperate with other apps, and allows you to cobble together just about anything, so it’s a good solution here. Anyway, when a file dialog appears on the screen, Default Folder X looks for and runs special AppleScripts if they’re present, allowing you to use any logic you want to determine the starting location (the “default folder”) for a file dialog, and the name of the file suggested in Save As dialogs.

The AppleScripts should be located in this folder:

     ~/Library/Application Support/com.stclairsoft.DefaultFolderX5/Scripts/

where the ‘~’ character denotes your home folder. Default Folder X looks for scripts with predetermined names, depending on their purpose:

  • getDefaultFilename.scpt – modifies the suggested filename in Save As dialogs
  • getDefaultFolder.scpt – specifies the default folder for an Open or Save dialog

Note that the scripts must be saved with those names in order for them to work. If you want a script to only run in the Open and Save dialogs of a single application, put it in a subfolder with the same name as the app. For example, to have scripts that only run in Safari, save them in:

     ~/Library/Application Support/com.stclairsoft.DefaultFolderX5/Scripts/Safari/

The implementation details (the name of the AppleScript handler and the arguments that Default Folder X passes to it) are included at the beginning of each of these sample scripts:

Truncating Long Filenames

OK, back to the task at hand. In this case, we want to provide a “getDefaultFilename” handler that truncates the default filename in Save dialogs to no more than “maxLength” characters.

Here’s the AppleScript:

on getDefaultFilename(appName, suggestedName, fileExtension)

  -- Specify our maximum filename length
  set maxLength to 45
  
  -- Make sure the filename extension isn't counted
  set addExtension to false
  if fileExtension is not "" and suggestedName ends with fileExtension then
    set suggestedName to text 1 thru -((count fileExtension) + 2) of suggestedName
    set addExtension to true
  end if
  
  -- Truncate the filename
  if length of suggestedName is greater than maxLength then
    set newName to text 1 thru maxLength of suggestedName
  else
    set newName to suggestedName
  end if
  
  -- Add the extension back on if appropriate
  if addExtension is true then
    set newName to newName & "." & fileExtension
  end if
  
  -- And give the name to Default Folder X
  return newName

end getDefaultFilename

Enter the code above in Script Editor and save it as an AppleScript script or download GetDefaultFilename.zip. Save the “GetDefaultFilename.scpt” file in this location:

     ~/Library/Application Support/com.stclairsoft.DefaultFolderX5/Scripts/

Each time a Save As dialog appears, it will magically truncate any filenames longer than 45 characters. Note that you can still edit the name afterwards in the Save dialog if you don’t like the one created by the script.

Credit Where Credit is Due

This scheme originated with an idea from Jason Snell (of Six Colors and The Incomparable fame) when he needed to customize the way Default Folder X worked in his podcasting workflow. I later expanded it as requests came in for customized filenames as well, and Bo Engelbrecht’s email reminded me that I’d never blogged about those updates. Thanks Jason and Bo!

Geek time: An excellent look into APFS

Tuesday, November 14th, 2017

This talk by Tim Standing (one of the developers behind SoftRAID) is an excellent analysis of APFS:

http://docs.macsysadmin.se/2017/video/Day3Session4.mp4

He has some very interesting points and conclusions – one of which is to never install APFS onto non-SSD (traditional spinning-platter) drives. The revelation that a major change was made to APFS very shortly before its release is also a little troubling, but at least that explains the current lack of documentation :-/

Thanks to Ronald Leroux for bringing this to my attention.

Repost: “Move Items” Finder contextual menu command

Tuesday, February 28th, 2017

This is information I originally posted back in 2009, but here we are 6 macOS versions later and it’s still relevant – and people are still asking for it.

Below are details on how to create a “Move Items” contextual menu item in the Services menu in the Finder (see the picture below).

It pops up a file dialog to let you specify where you want to move the selected items, which then allows you to use Default Folder X, giving you access to all of your Favorite and Recent folders, Finder-click, etc.

TL;DR: If you just want to get things to work, there’s a link at the bottom to download an already-built Automator workflow.


Contextual menu items are added by making a Workflow in Automator and saving it as a Service. You start by running the Automator application (it’s in your /Applications folder), creating a new Workflow, and setting it up as shown in the (old) screenshots below:

If you’re experienced with Automator, you’re probably asking: Why go to the trouble of creating variables instead of just using the “Move Finder Items” action by itself?  I’m glad you asked!  The reason is that I want to bring up a file dialog to specify the folder where I want the items to go.  There’s not a clean way to have the “Move Finder Items” do that every time.  You can change its options to “Show this action when the workflow runs” but you still have to click on it every time you use it to ask it to show a file dialog.  If you use Default Folder X to enhance your Open dialogs, it’s faster to just have the dialog pop up and then go where you want to with DFX.

So in the image above, the workflow puts the current Finder selection into the “selection” variable.  Then it uses AppleScript to bring up a file dialog to ask for a folder, which it stores in the “path” variable.  And finally, it uses the Move Finder Items action to do the work.  Not too much more complicated, and it speeds up your workflow considerably if you’ve already got DFX installed so the Open dialogs are smart.

For you Automator programmers, note that some of the actions shown in the workflow do not take inputs.  I did this by control-clicking on the action (“Get Value of Variable”, for example) and choosing “Ignore Input” from the contextual menu.  If you don’t do this, Automator will actually add the input from the previous step to the next one, which is definitely not what you want in this case.

Oh, and if you just want the automator workflow file so you can add it to your own system, you can download it here:

https://www.stclairsoft.com/download/MoveItems.zip

If you need more help with Automator and Services, Sal Soghoian has some good information and tutorials here:

https://www.macosxautomation.com/services/learn/

(Once you’ve gotten through the first few steps of the tutorial, you should be able to just replicate the picture above to make the Move Items service yourself).

Developer Tip: Avoiding the Discrete GPU

Friday, December 16th, 2016

So I’ve run into this issue both as a user and as a developer – on MacBook Pros that have both a discrete and integrated GPU, fancy animations will cause the system to switch to the more powerful discrete GPU, reducing battery life. Chris Liscio wrote a great post explaining what’s going on and what to do about it (from a developer’s perspective). The key takeaway for most developers:

This whole problem can be very easy to solve. You just have to set NSSupportsAutomaticGraphicsSwitching key to YES in your application’s Info.plist. The trouble is that an OpenGL context is being created, which defaults to switching the dGPU on. Enabling this flag in the plist will very likely fix the problem on its own, as the frameworks should Do the Right Thing (more details below) if they need access to OpenGL.

Go read his whole post for all the details.

 

Auto-Update Vulnerability in Sparkle

Wednesday, February 10th, 2016

A security vulnerability has been found in Sparkle, the framework used by many Mac applications to check for and download software updates automatically. Full details are at:

http://arstechnica.com/security/2016/02/huge-number-of-mac-apps-vulnerable-to-hijacking-and-a-fix-is-elusive/

While some of our applications (like HistoryHound) are using older versions of the Sparkle framework at the moment, they all use encrypted HTTPS connections to check for and download updates, so there’s no chance of a man-in-the-middle attack, as described in the report.

So you can safely leave automatic update checking turned on in all of our products – it’s being done safely.

– Jon

We interrupt your normally scheduled Default Folder X development for… expiring App Store receipts??

Thursday, November 12th, 2015

The Problem:

So I started getting emails yesterday complaining that Jettison was suddenly telling users their trial period was over – even though they’d already purchased a license. When I got the first few, I thought they’d just deleted their preference files and needed to re-activate their licenses, but then the trickle became a deluge – what the heck?

So I dropped everything and looked into it – I needed an answer ASAP or I was gonna spend the next couple of days doing nothing but answering email. It turns out everyone who was affected had bought Jettison through the Mac App Store and then upgraded to the direct-from-the-website version (because it’s better, of course – instructions here if you’re interested). When you do this, Jettison copies your Mac App Store receipt to a safe place so that it can verify that you’ve actually bought a license, even if you delete the App Store copy of Jettison.

Lucky for me, I’d bought a copy of Jettison myself when testing this mechanism, so I had my own receipt still sitting in ~/Library/Application Support/ so I could look at it. Printing the certificates in the receipt showed this little tidbit:

[...]
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations [...]
Validity
    Not Before: Nov 11 21:58:01 2010 GMT
    Not After : Nov 11 21:58:01 2015 GMT
Subject: CN=Mac App Store Receipt Signing, OU=Apple Worldwide Deve [...]
[...]

See that “Not After:” entry in the Validity section? “Nov 11 21:58:01 2015 GMT” – yeah, that’d be yesterday. When the emails started. Apple signed the receipt with a certificate that expired yesterday, so if you have one of these receipts, Jettison no longer thinks you’re legit. Sorry about that – I hadn’t considered that eventuality. And reading the news this morning, it appears that Apple hadn’t either.

The Fix:

So what to do? I’ve wrapped up Jettison 1.5 and posted it. You’re going to have to do a little dance again to get Jettison to update your receipt, but this version will do the right thing once you follow these instructions:

  1. Put every copy of Jettison on your Mac in the Trash and empty the Trash.
  2. Open the App Store application and click on the Purchases tab.
  3. Re-download the copy of Jettison you purchased. It will include a new, non-expired receipt.
  4. Download the latest version of Jettison (http://www.stclairsoft.com/download/Jettison-1.5.1d2.zip)
  5. Double-click the .dmg file to open it, then double-click on Jettison before copying it to your Applications folder.
  6. After Jettison tells you that it has found your App Store license, you can copy it to your Applications folder.

Sorry for the hassle. But hey, at least it forced me to get the version 1.5 update out the door, so there’s some benefit there, eh? And thanks Apple – I didn’t need to sleep last night anyway.

– Jon

P.S. I’m seeing a bunch of people buying non-App Store licenses directly from the St. Clair Software store today instead of jumping through these hoops to deal with the App Store. I have to say I’m all for that 🙂

Update:

A bit more info that’s interesting and could use some corroboration: I think this problem only affects apps that were downloaded before September 24 (either via purchase or update). When I download a new copy of Jettison from the App Store, the receipt is signed with a cert valid within these dates:

            Not Before: Sep 24 19:09:31 2015 GMT
            Not After : Oct 23 19:09:31 2017 GMT

So in my sample size of 1, copies of Jettison purchased or updated today will work until Oct 23, 2017, and could have worked with this receipt only as far back as September 24. If Apple has been using the same certificate to sign all App Store receipts (which seems reasonable), then anything that has been downloaded from the App Store after September 24 won’t expire until 2017. And apps downloaded prior to that have some other expiration in their receipts. If I had more time, I’d dig through all of my App Store apps to find out when each cert expires, but alas, I’ve got work to do and have killed enough time on this already…

Kubra / BC Hydro – How NOT to Do Online Payments

Monday, June 18th, 2012

Kudos to Kubra and BC Hydro – they fixed this problem within 8 hours of receiving my report and are reviewing some of their practices as well. If you’re a BC Hydro customer and have made online payments in the past, you still need to make sure to clean any of the old URLs out of your browser history.

Notice anything disconcerting about this URL?

https://secure3.i-doxs.net/BCHydro/OneTime_PayProcess.asp?PaymentDate=6%2F6%2F2012&AccountNumber=1234567&InvoiceNumber=&PayAmount=38.05& Description=&CustomerEmail=jon%40email.net&CustomerName=Jon+Gotow&PaymentType=CC& State=&ConvenienceFee=2&CCCardName=VISA&CCHolderName=Jon+Gotow& CCNumber=4123456789012345&CCCVV2=123&CCMonth=1&CCYear=15&AVSname=& AVSphone=&AVSaddress1=&AVSaddress2=&AVScity=& AVSstate=&AVSzip=

I found it in my browser history after making an online payment for a BC Hydro electricity bill. And yes, you’re not mistaken. That URL has my account number, name, email address, credit card number, credit card expiration date and CVV number all embedded in it. (Yes, I did change them before blogging about this 🙂

Really?

Honestly, this has got to be one of the biggest, dumbest security mistakes I’ve seen in ages. I mean, really? Just toss all my credit card information into my browser history in clear text? Then let Google, Apple, or Mozilla sync it to all my devices and to the Cloud – so anyone can access it anywhere – brilliant!

What’s more, clicking on that URL submits another payment directly to their system. Go ahead – click on it! You know you want to! If the numbers were still correct, you’d rack up hundreds of dollars in charges to my credit card with just a few clicks of your mouse. How do I know? Because I mistakenly did that.

The Bottom Line

Kubra, the company behind this ridiculous little payment gateway, was absolutely no help when I called. They can’t refund the payments, including their own $2 “convenience fee” – they told me to dispute the charges with my credit card company instead. And when I asked them to address the underlying security flaw, they said they couldn’t do anything without a request from BC Hydro. So I’ve contacted BC Hydro through their web site. If you’re a BC Hydro customer, I encourage you to complain to them too – and NOT pay your power bill online until they get this resolved.

And if you’ve paid a bill via Kubra in the past, go check your browser history and see if your credit card or bank information is conveniently stored there for you (and everyone else). If it is, it might be a good idea to delete it 😉

That’s Cool! How Do I Get an URL Like That?

If you’ve got a BC Hydro account, you can see this for yourself. Go to http://www.bchydro.com/ and log into your account. Click on “Ways to Pay,” then “Online banking or credit card payment,” then “Kubra” as shown below.

You’ll get a data entry screen like this. Just fill in your credit card data, click “Submit”, and then confirm your payment on the next page. Then copy the URL in your browser’s address bar or from your browser history. There you go!

* Unfortunately, you do have to enter valid credit card and account information into this window to get back a valid URL. Otherwise you just get an URL filled with error message information – not nearly as fun.

Update #1

I just got a call from BC Hydro – kudos to them for moving so quickly! It’s been a mere few hours since they read the email I sent them over the weekend and they’re already moving on it.

Update #2

Impressive! Kubra just called and they’ve plugged the hole on their end (it looks like the switched from HTTP GET requests to HTTP POST) so the data no longer ends up in the URL. They’re also refunding the erroneous charges caused by me going to those URLs in the first place.

So the problem is fixed! The only issue that remains: If you’re a BCHydro customer and have paid your bill online in the past, search your browser history for “BCHydro” and delete any history items that match. (That’s something that you have to fix – BCHydro and Kubra can’t erase data that’s already on your computer).

Popping up Default Folder X’s menus with a hotkey

Tuesday, March 20th, 2012

I’ve had requests from a number of people asking for a hotkey to pop up Default Folder X’s menus under the mouse. That way you don’t have to go all the way up to the menubar to get to your Favorite and Recent folders. Default Folder X doesn’t currently have a keyboard shortcut for this, but you can make one easily with any macro utility that supports AppleScript. Keyboard Maestro is one example.

Just configure your macro program to run this AppleScript:

    with timeout of 0 seconds
        ignoring application responses
            tell application "Default Folder X"
                ShowMenu
            end tell
        end ignoring
    end timeout

That’s all there is to it!  Thanks to Scott Mintzer for jogging my memory on this.

Update – Andrew Gara wrote to remind me: If you’re running Default Folder X with its “Show icons and menus in the Dock” setting turned off, the application target in the AppleScript should be “Default Folder X Helper” instead of “Default Folder X”. Thanks Andrew!

Default Folder X 4.3.4 Released

Tuesday, December 1st, 2009

I’ve fixed a number of things in version 4.3.4 of Default Folder X.  If you’re using Default Folder X, you should grab the update – it’s free, and will run more smoothly on your system.  A full list of changes is available on the release page, or you can just download the installer here:

http://www.stclairsoft.com/cgi-bin/dl.cgi?DX

Thanks to everyone for their feedback and assistance with testing!

Being careful with LSSharedFileListAddObserver

Friday, November 6th, 2009

So, Apple added this cool little capability to the Launch Services API in Leopard: LSSharedFileListAddObserver will call your observer function whenever there are changes in a number of different file lists maintained by Launch Services. One of those lists is the “Recent Documents” list in the Apple Menu. “Great!” I thought, “I’ll roll this into Default Folder X to ensure that it doesn’t miss any recently used folders.”  It’s a simple API – what could go wrong?  As a long time developer, I should have known better – if you EVER say this (even if you never even say it out loud), you need to poke yourself with something sharp and realize that the consequences will probably hurt quite a bit more than that. “What could go wrong?” indeed.

So yes, here I am apologizing for not having poked myself after I used LSSharedFileListAddObserver without asking more questions – or at least without testing more.  Here’s how Default Folder X ended up using 60% of some users’ CPUs while doing nothing useful:

  1. DFX added itself as an observer for kLSSharedFileListRecentDocumentItems.
  2. The observer function got called by Launch Services because the user double-clicked a document in the Finder.
  3. DFX looked at the list, took the most recent entry in it (the first one), asked Launch Services for the URL of the document, and added the folder enclosing that document to its Recent Folders list.

Pretty simple, right? Yeah, I thought so too. This was tested thoroughly on Snow Leopard and performed fine, and all my Leopard testers reported that it worked well for them too.

So what’s the problem then?  Well, there’s this little “issue”…  If a user has a Windows server mounted on the Desktop, things get a little more interesting.  Normally, when Launch Services calls your observer function, it hands you the file list and you ask for a copy of the list.  The list itself is just a series of ID’s and references – to see what’s in an entry, you have to call LSSharedFileListItemResolve().  And that’s where the interesting part happened.  On Leopard, if the shared file list item lies on a Windows server, the act of calling LSSharedFileListItemResolve actually results in the item being changed, so your observer function gets called again the next time you hit your event loop.  The result of this is that you get called over and over again if you naively use LSSharedFileListItemResolve to get more info about the items that Launch Services is handing you.

So – the warning:  If you use LSSharedFileListAddObserver to watch the list of recent documents, keep a copy of the ID’s from the previous call and ONLY call LSSharedFileListItemResolve if there’s a new ID in the array.  Otherwise do nothing, or work off cached information – otherwise you’ll end up in an infinite loop, sucking down lots of CPU time.  And if you’re doing anything that interacts with the filesystem, make SURE you test with SMB shared volumes too.