Module: Xolo::Server::Helpers::FileTransfers

Defined in:
lib/xolo/server/helpers/file_transfers.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(includer) ⇒ Object

when this module is included



28
29
30
# File 'lib/xolo/server/helpers/file_transfers.rb', line 28

def self.included(includer)
  Xolo.verbose_include includer, self
end

Instance Method Details

#pkg_is_distribution?(pkg_file) ⇒ Boolean

Check if a package is a Distribution package, if not, it is a component package and can’t be used for MDM deployment.

Parameters:

  • pkg_file (Pathname, String)

    The path to the .pkg

Returns:

  • (Boolean)

    true if the pkg is a Distribution package



181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/xolo/server/helpers/file_transfers.rb', line 181

def pkg_is_distribution?(pkg_file)
  pkg_file = Pathname.new(pkg_file)
  raise ArgumentError, "pkg_file does not exist or not a file: #{pkg_file}" unless pkg_file.file?

  tmpdir = Pathname.new(Dir.mktmpdir)
  workdir = tmpdir + "#{pkg_file.basename}-expanded"

  system "/usr/sbin/pkgutil --expand #{pkg_file.to_s.shellescape} #{workdir.to_s.shellescape}"

  workdir.children.map(&:basename).map(&:to_s).include? 'Distribution'
ensure
  tmpdir.rmtree
end

#process_incoming_pkgObject

Handle an uploaded pkg installer TODO: wrap this in a thread, it might be very slow for large pkgs. TODO: Also, when threaded, how to report errors? TODO: Split this into smaller methods



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/xolo/server/helpers/file_transfers.rb', line 77

def process_incoming_pkg
  log_info "Processing uploaded installer package for version '#{params[:version]}' of title '#{params[:title]}'"

  # the Xolo::Server::Version that owns this pkg
  version = instantiate_version title: params[:title], version: params[:version]
  version.lock

  # is this a re-upload? True if upload_date as any value in it
  if version.upload_date.pix_empty?
    action = 'Uploading'
    re_uploading = false
  else
    re_uploading = true
    action = 'Re-uploading'
    version.log_change msg: 'Re-uploading pkg file'
  end

  # the original uploaded filename
  orig_filename = params[:file][:filename]
  log_debug "Incoming pkg file '#{orig_filename}' "
  file_extname = validate_uploaded_pkg(orig_filename)

  # Set the jamf_pkg_file, now that we know the extension
  uploaded_pkg_name = "#{version.jamf_pkg_name}#{file_extname}"
  log_debug "Jamf: Package filename will be '#{uploaded_pkg_name}'"
  version.jamf_pkg_file = uploaded_pkg_name

  # The tempfile created by Sinatra when the pkg was uploaded from xadm
  tempfile = Pathname.new params[:file][:tempfile].path

  # The uploaded tmpfile will be staged here before uploading again to
  # the Jamf Dist Point(s)
  staged_pkg = Xolo::Server::Title.title_dir(params[:title]) + uploaded_pkg_name

  # remove any old one that might be there
  staged_pkg.delete if staged_pkg.file?

  if need_to_sign?(tempfile)
    # This will put the signed pkg into the staged_pkg location
    sign_uploaded_pkg(tempfile, staged_pkg)
    log_debug "Signing complete, deleting temp file '#{tempfile}'"
    tempfile.delete if tempfile.file?
  else
    log_debug "Uploaded .pkg file doesn't need signing, moving tempfile to '#{staged_pkg.basename}'"
    # Put the signed pkg into the staged_pkg location
    tempfile.rename staged_pkg
  end

  # upload the pkg with the uploader tool defined in config
  # This will set the checksum and manifest in the JPackage object
  upload_to_dist_point(version.jamf_package, staged_pkg)

  if re_uploading
    # These must be set before calling wait_to_enable_reinstall_policy
    version.reupload_date = Time.now
    version.reuploaded_by = session[:admin]

    # This will make the version start a thread
    # that will wait some period of time (to allow for pkg uploads
    # to complete) before enabling the reinstall policy
    version.wait_to_enable_reinstall_policy
  else
    version.upload_date = Time.now
    version.uploaded_by = session[:admin]
  end

  # make note if the pkg is a Distribution package
  version.dist_pkg = pkg_is_distribution?(staged_pkg)

  # save the manifest just in case
  version.manifest_file.pix_atomic_write(version.jamf_package.manifest)

  # save the checksum just in case
  version.sha_512 = version.jamf_package.checksum

  # don't save the admins local path to the pkg, just the filename they uploaded
  version.pkg_to_upload = orig_filename

  # save/update the local data file, since we've done stuff to update it
  version.save_local_data

  # log the upload
  version.log_change msg: "#{action} pkg file '#{staged_pkg.basename}'"

  # remove the staged pkg and the tempfile
  staged_pkg.delete
  tempfile.delete if tempfile.file?
rescue => e
  msg = "#{e.class}: #{e}"
  log_error msg
  e.backtrace.each { |line| log_error "..#{line}" }
  halt 400, { status: 400, error: msg }
ensure
  version.unlock
end

#process_incoming_ssvc_iconObject

Store an uploaded self service icon in the title’s directory. It’ll be added to Policies and Patch Policies as needed (increasing the bloat in the database, of course)



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/xolo/server/helpers/file_transfers.rb', line 56

def process_incoming_ssvc_icon
  filename = params[:file][:filename]
  tempfile = Pathname.new params[:file][:tempfile].path

  log_info "Processing uploaded SelfService icon for #{params[:title]}"
  title = instantiate_title params[:title]
  title.save_ssvc_icon(tempfile, filename)
  title.configure_pol_for_self_service if title.self_service
rescue => e
  msg = "#{e.class}: #{e}"
  log_error msg
  e.backtrace.each { |line| log_error "..#{line}" }

  halt 400, { status: 400, error: msg }
end

#process_incoming_testfileObject

upload a file for testing … anything



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/xolo/server/helpers/file_transfers.rb', line 38

def process_incoming_testfile
  progress 'starting test file upload', log: :debug

  params[:file][:filename]
  tempfile = Pathname.new params[:file][:tempfile].path

  progress "1/3 TempFile is #{tempfile} size is #{tempfile.size}... is it still uploading?", log: :debug
  sleep 2
  progress "2/3 TempFile is #{tempfile} size is #{tempfile.size}... is it still uploading?", log: :debug
  sleep 2
  progress "3/3 TempFile is #{tempfile} size is #{tempfile.size}... is it still uploading?", log: :debug
  progress 'all done', log: :debug
end

#upload_to_dist_point(jpkg, pkg_file) ⇒ void

This method returns an undefined value.

upload a staged pkg to the dist point(s) This will also update the checksum and manifest.

Parameters:

  • jpkg (Jamf::JPackage)

    The package object for which the pkg is being uploaded

  • pkg_file (Pathname)

    The path to .pkg file being uploaded



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/xolo/server/helpers/file_transfers.rb', line 203

def upload_to_dist_point(jpkg, pkg_file)
  if Xolo::Server.config.upload_tool.to_s.downcase == 'api'
    jpkg.upload pkg_file # this will update the checksum and manifest automatically, and save back to the server
    log_info "Jamf: Uploaded #{pkg_file.basename} to primary dist point via API, with new checksum and manifest"
  else
    log_debug "Jamf: Regenerating manifest for package '#{jpkg.packageName}' from #{pkg_file.basename}"
    jpkg.generate_manifest(pkg_file)

    log_debug "Jamf: Recalculating checksum for package '#{jpkg.packageName}' from #{pkg_file.basename}"
    jpkg.recalculate_checksum(pkg_file)

    log_info "Jamf: Saving package '#{jpkg.packageName}' with new checksum and manifest"
    jpkg.save
    upload_via_tool(jpkg, pkg_file)
  end
end

#upload_via_tool(jpkg, pkg_file) ⇒ void

This method returns an undefined value.

upload the pkg with the uploader tool defined in config

Parameters:

  • version (Xolo::Server::Version)

    The version object

  • staged_pkg (Pathname)

    The path to the staged pkg



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/xolo/server/helpers/file_transfers.rb', line 227

def upload_via_tool(jpkg, pkg_file)
  log_info "Jamf: Uploading #{pkg_file.basename} to dist point(s) via upload tool"

  tool = Shellwords.escape Xolo::Server.config.upload_tool.to_s
  jpkg_name = Shellwords.escape jpkg.packageName
  pkg = Shellwords.escape pkg_file.to_s
  cmd = "#{tool} #{jpkg_name} #{pkg}"

  stdouterr, exit_status = Open3.capture2e(cmd)
  return if exit_status.success?

  msg =  "Uploader tool failed to upload #{pkg_file.basename} to dist point(s): #{stdouterr}"
  log_error msg
  raise msg
end

#validate_uploaded_pkg(filename) ⇒ String

Confirm and return the extension of the originally uplaoded file, either .pkg or .zip

Parameters:

  • filename (String)

    The original name of the file uploaded to Xolo.

Returns:

  • (String)

    either ‘.pkg’ or ‘.zip’



250
251
252
253
254
255
256
257
# File 'lib/xolo/server/helpers/file_transfers.rb', line 250

def validate_uploaded_pkg(filename)
  log_debug "Validating pkg file ext for '#{filename}'"

  file_extname = Pathname.new(filename).extname
  return file_extname if Xolo::OK_PKG_EXTS.include? file_extname

  raise "Bad filename '#{filename}'. Package files must end in .pkg or .zip (for old-style bundle packages)"
end