Best way to update my own software

Hi,

in your opinion, what is the best approach to manage the user updates of my own software?
I trying with FTP using TCPSocket, but is not very easy, so I was thinking to a better solution.

Thanks.

What about placing an XML file or other format easily readable on a web page. The file could contain a version number, change log and URL to the update. Your program could then check to see if it is up-to-date, if not display a dialog showing the change log with a download or cancel button. If they want to download it, use the http class to download the update.

I wrote a little procedure using sublclassed HTTPSOCKET
It goes to my website… downloads a one line file containing version information, conpares that to version information internal to the program… and if the server data is more recent… it tells the user and download the updated file

LOL…beat me by 2 seconds :slight_smile:

so… then here :slight_smile:

presented AS-is… up to you to decide if you can make it work for you

  #If Not DebugBuild
    #pragma DisableBackgroundTasks
    #pragma DisableBoundsChecking
    #pragma DisableAutoWaitCursor
    #pragma StackOverflowchecking False
    #pragma NilObjectChecking False
  #EndIf
  Dim http As New classHTTPSocket
  Dim i As Integer
  Dim s As String
  Dim v(-1) As String
  Dim vv(-1) As String
  Dim d As New MessageDialog
  Dim b As MessageDialogButton
  Dim f As FolderItem
  Dim isMounted As Boolean
  Dim mount_msg As String
  Dim Update_Status As Integer
  Dim download_site As String
  Dim file_fmt As String
  Dim fmt_cur_version As String
  fmt_cur_version=format_version(pgm_version+"."+Str(app.NonReleaseVersion))
  mount_msg=""
  isMounted=False
  
  download_site=main_website+"/"+pgm_name+"/"
  
  // Assumptions :
  //
  // winMAIN is the name of the window to show this dialog in.
  //
  // you have a subclass of HTTPSocket called classHTTPSocket [see above]
  //
  // there are global variables in the application
  //   pgm_name - descriptive name of the app
  //   pgm_version - String with current version # [ie. s="0.22"]
  //
  //   a global procedure called "msgNeedtoSave" which checks if any changes need to be saved first
  //
  //   your server has two files at the web address above
  //      a) xxx.dmg - dmg file with new version of code in it
  //      b) xxx.ver - one line text file [see below for format]
  //
  For i=0 To VolumeCount-1
    If Volume(i).DisplayName=pgm_name Then
      isMounted=True
      mount_msg=EndOfLine+EndOfLine+"'"+pgm_name+"' is already mounted on your desktop, please"+EndOfLine+"eject this file and try again."
      Exit For
    End If
  Next i
  
  d.icon=MessageDialog.GraphicNote
  d.ActionButton.Visible=True
  d.ActionButton.Caption="Ok"
  d.CancelButton.Visible=False
  d.AlternateActionButton.Visible=False
  http.Yield=True
  s=Trim(http.Get(Lowercase(download_site+pgm_name+".ver"),4))
  Update_Status=0
  If s<>"" Then
    // format xx.xxxx.xxx mm/dd/yyyy ZIP
    // version and date uploaded
    v=Split(s," ")
    //
    // reformat to match fmt_cur_version
    v(0)=format_version(v(0))
    file_fmt="DMG"
    If Val(ReplaceAll(v(0),".",""))>0 Then
      If v(0)=fmt_cur_version Then Update_Status=1
      If v(0)>fmt_cur_version Then Update_Status=2
      If v(0)<fmt_cur_version Then Update_Status=3
    Else
      Update_Status=-1
      If v.Ubound=1 Then Update_Status=1
    End If
  Else
    Update_Status=-1
  End If
  If v.Ubound>1 Then file_fmt=v(2)
  '
  v(0)=format_version(v(0),True)
  fmt_cur_version=format_version(fmt_cur_version,True)
  
  Select Case Update_Status
  Case -1
    d.icon=MessageDialog.GraphicCaution
    d.message="Unable to connect to remote server"
    d.explanation="In order to obtain current version information"+EndOfLine+download_site+pgm_name+".ver"
  Case 1
    d.message="You are up to date..."
    d.Explanation=Pgm_Name+" "+fmt_cur_version+" is currently the latest version available."
    If Not notify Then Update_Status=0
  Case 2
    d.Message="An update to "+Pgm_Name+" is available"
    d.explanation="You currently have "+fmt_cur_version+" and "+v(0)+" was made"+EndOfLine+"available on "+v(1)+mount_msg+EndOfLine+"Download file is in "+file_fmt+" format."
    If Not isMounted Then
      d.CancelButton.Visible=True
      d.CancelButton.Caption="View Release Notes"
      d.AlternateActionButton.Visible=True
      d.AlternateActionButton.Caption="Download version "+v(0)
      d.ActionButton.Visible=True
      d.ActionButton.Caption="Skip it for now"
    End If
  Case 3
    d.Message="Somebody screwed up...."
    d.explanation="You currently have "+fmt_cur_version+", but the Server has "+v(0)+" created on "+v(1)
    d.ActionButton.Caption="Opps"
    If Not notify Then Update_Status=0
  End Select
  
  If Update_Status<>0 Then
    b=d.ShowModalwithin(winMAIN)
    Select Case b
    Case d.ActionButton
      'exit sub
    Case d.AlternateActionButton
      /////winMAIN.lblDOWNLOAD.Visible=true
      /////winMAIN.lblDOWNLOAD.Invalidate
      s=Lowercase(pgm_name+"."+file_fmt)
      f=SpecialFolder.Desktop.Child(s) // saves the file on Desktop
      If f.exists Then f.Delete
      http.Yield=False
      s=Lowercase(download_site+s)
      http.get (s,f)  //downloading file  to desktop
      
    Case d.CancelButton
      //user pressed Cancel
      // View Release Notes
      /////ShowURL web_release_notes
    End Select
  End If
  
  
  

Thanks guys.
I choosen xml solution.

We do something very similar, except we use PHP on the server, we send the nonReleaseVersion when checking for an update and the PHP returns relevant information.

Then we use a HTTPSocket to download the update.

I’m still investigating a solution for Sandboxed applications however.

A sandboxed application should be able to install a privileged helper using SMJobBless. That helper would handle the updating. My plan is for the helper to download an updater and execute it.

I haven’t written any code or even seriously investigated this, so there may be some very big gotchas, but the theory makes sense. It also has the advantage of being fairly compatible with Windows, where the helper would be a service.

In theory it should, I know some people have experimented with XPC and hit one of the brick walls that stopped me. I can’t create or move the app to the applications folder without the user actually choosing it. You can add a TE to allow the app to write anywhere on the disk, but then why bother Sandboxing it?

There is a new entitlement (which I can’t figure out which version it was introduced) which allows an app to create apps.

Well the helper would not be sandboxed.

dont forget “man in the middle” attacks on that mechanism. Equipped with a traffic/network sniffer it’s quite easy to see who is asking where on what XML/HTTP services. My advice: Use at least HTTPS to check such XML file on your server.

Second how does your software “see” that the downloaded update is “yours”? With an man in the middle attack somebody could manipulate his own DNS server, downloading your XML file and replacing your Update Executable File with his Malware. You should use checksums or at least check SSL certificates in case you’re using HTTPS.

Very good point Tomas, while Apple will of course pop-up the “Are you sure you want to run this app from developer XYZ”, I wonder how many users actually read it!

Although a checksum and HTTPS wouldn’t do anything to stop someone who broke into your server, you could however validate the code signature. The only way that can fail is if someone is using a cracked version of your app, and at which point I don’t really give a toss if they download an infected application!

Aha - the solution is to use an Apple installer, not very elegant mind you, but that will have permission to update your application. If you download the installer package to your app support folder, you will be able to execute it.

It does mean having two versions, one .zip and one .pkg or just go the pkg route.

Man, I love these conversations.

I think it can be done without an installer.

Validating the code signature is not enough, in my opinion. You really need to distribute the compressed archive along with a signature on that. The app has the public key which did the signing, then just has to verify everything matches. Using the app’s code signature means the archive has to be decompressed, which is further than I’d like to see an attack get.

You can code sign installer packages, but I never tried to sign a zip file… I wonder…

You can sign anything. The signature wouldn’t get embedded in the zip, but you can sign it.

That’s the concern is getting a signature that can easily be tested against, but it virtually impossible to fake.

Well… right. You need RSA public / private keys. As long as your app’s binary has not been altered, the public key can verify the signature with 100% certainty. The verification process takes 3 things: the data (zip), the signature, and the public key. If those three things don’t match perfectly, verification fails. So you’d know the zip provided was “official.”

…and in case you lost your private RSA keys, just ask the NSA. They will send you copy of it :wink:

You could also use our UpdaterKit for adding an updater feature to your app.
No need to code that all again.