| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  | # typed: true | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | require "development_tools" | 
					
						
							| 
									
										
										
										
											2020-08-09 01:34:07 +02:00
										 |  |  | require "cask/exceptions" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 08:29:14 +02:00
										 |  |  | module Cask | 
					
						
							| 
									
										
										
										
											2020-08-24 23:44:12 +02:00
										 |  |  |   # Helper module for quarantining files. | 
					
						
							|  |  |  |   # | 
					
						
							|  |  |  |   # @api private | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |   module Quarantine | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  |     QUARANTINE_ATTRIBUTE = "com.apple.quarantine" | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 19:39:07 +01:00
										 |  |  |     QUARANTINE_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/utils/quarantine.swift").freeze | 
					
						
							| 
									
										
										
										
											2023-04-03 20:21:37 -05:00
										 |  |  |     COPY_XATTRS_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/utils/copy-xattrs.swift").freeze | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.swift | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |       @swift ||= DevelopmentTools.locate("swift") | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     private_class_method :swift | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.xattr | 
					
						
							| 
									
										
										
										
											2018-09-26 20:55:54 +00:00
										 |  |  |       @xattr ||= DevelopmentTools.locate("xattr") | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     private_class_method :xattr | 
					
						
							| 
									
										
										
										
											2018-09-26 20:55:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.swift_target_args | 
					
						
							| 
									
										
										
										
											2022-06-02 18:53:46 +01:00
										 |  |  |       ["-target", "#{Hardware::CPU.arch}-apple-macosx#{MacOS.version}"] | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     private_class_method :swift_target_args | 
					
						
							| 
									
										
										
										
											2022-06-02 18:53:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-20 12:03:48 +02:00
										 |  |  |     sig { returns(Symbol) } | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.check_quarantine_support | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |       odebug "Checking quarantine support" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-25 21:22:06 +08:00
										 |  |  |       if xattr.nil? || !system_command(xattr, args: ["-h"], print_stderr: false).success? | 
					
						
							| 
									
										
										
										
											2021-01-24 21:40:41 -05:00
										 |  |  |         odebug "There's no working version of `xattr` on this system." | 
					
						
							| 
									
										
										
										
											2018-09-26 20:55:54 +00:00
										 |  |  |         :xattr_broken | 
					
						
							|  |  |  |       elsif swift.nil? | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |         odebug "Swift is not available on this system." | 
					
						
							| 
									
										
										
										
											2018-09-14 15:48:16 +00:00
										 |  |  |         :no_swift | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         api_check = system_command(swift, | 
					
						
							| 
									
										
										
										
											2022-06-02 18:53:46 +01:00
										 |  |  |                                    args:         [*swift_target_args, QUARANTINE_SCRIPT], | 
					
						
							| 
									
										
										
										
											2018-09-14 15:48:16 +00:00
										 |  |  |                                    print_stderr: false) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case api_check.exit_status | 
					
						
							|  |  |  |         when 2
 | 
					
						
							|  |  |  |           odebug "Quarantine is available." | 
					
						
							|  |  |  |           :quarantine_available | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           odebug "Unknown support status" | 
					
						
							|  |  |  |           :unknown | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.available? | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |       @status ||= check_quarantine_support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @status == :quarantine_available | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.detect(file) | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |       return if file.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       odebug "Verifying Gatekeeper status of #{file}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       quarantine_status = !status(file).empty? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       odebug "#{file} is #{quarantine_status ? "quarantined" : "not quarantined"}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       quarantine_status | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.status(file) | 
					
						
							| 
									
										
										
										
											2018-09-26 20:55:54 +00:00
										 |  |  |       system_command(xattr, | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |                      args:         ["-p", QUARANTINE_ATTRIBUTE, file], | 
					
						
							|  |  |  |                      print_stderr: false).stdout.rstrip | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.toggle_no_translocation_bit(attribute) | 
					
						
							| 
									
										
										
										
											2018-09-26 20:55:54 +00:00
										 |  |  |       fields = attribute.split(";") | 
					
						
							| 
									
										
										
										
											2018-09-07 15:37:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       # Fields: status, epoch, download agent, event ID | 
					
						
							|  |  |  |       # Let's toggle the app translocation bit, bit 8 | 
					
						
							| 
									
										
										
										
											2018-10-03 21:03:22 +00:00
										 |  |  |       # http://www.openradar.me/radar?id=5022734169931776 | 
					
						
							| 
									
										
										
										
											2018-09-07 15:37:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       fields[0] = (fields[0].to_i(16) | 0x0100).to_s(16).rjust(4, "0") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       fields.join(";") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.release!(download_path: nil) | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       return unless detect(download_path) | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       odebug "Releasing #{download_path} from quarantine" | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-26 20:55:54 +00:00
										 |  |  |       quarantiner = system_command(xattr, | 
					
						
							| 
									
										
										
										
											2018-11-02 17:18:07 +00:00
										 |  |  |                                    args:         [ | 
					
						
							| 
									
										
										
										
											2018-09-14 15:48:16 +00:00
										 |  |  |                                      "-d", | 
					
						
							|  |  |  |                                      QUARANTINE_ATTRIBUTE, | 
					
						
							|  |  |  |                                      download_path, | 
					
						
							|  |  |  |                                    ], | 
					
						
							|  |  |  |                                    print_stderr: false) | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       return if quarantiner.success? | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       raise CaskQuarantineReleaseError.new(download_path, quarantiner.stderr) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2018-09-07 15:37:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.cask!(cask: nil, download_path: nil, action: true) | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       return if cask.nil? || download_path.nil? | 
					
						
							| 
									
										
										
										
											2018-09-07 15:37:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       return if detect(download_path) | 
					
						
							| 
									
										
										
										
											2018-09-07 15:37:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       odebug "Quarantining #{download_path}" | 
					
						
							| 
									
										
										
										
											2018-09-07 15:37:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       quarantiner = system_command(swift, | 
					
						
							| 
									
										
										
										
											2018-11-02 17:18:07 +00:00
										 |  |  |                                    args:         [ | 
					
						
							| 
									
										
										
										
											2022-06-02 18:53:46 +01:00
										 |  |  |                                      *swift_target_args, | 
					
						
							| 
									
										
										
										
											2018-09-14 15:48:16 +00:00
										 |  |  |                                      QUARANTINE_SCRIPT, | 
					
						
							|  |  |  |                                      download_path, | 
					
						
							|  |  |  |                                      cask.url.to_s, | 
					
						
							|  |  |  |                                      cask.homepage.to_s, | 
					
						
							|  |  |  |                                    ], | 
					
						
							|  |  |  |                                    print_stderr: false) | 
					
						
							| 
									
										
										
										
											2018-09-07 15:37:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 14:00:44 +00:00
										 |  |  |       return if quarantiner.success? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       case quarantiner.exit_status | 
					
						
							|  |  |  |       when 2
 | 
					
						
							|  |  |  |         raise CaskQuarantineError.new(download_path, "Insufficient parameters") | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         raise CaskQuarantineError.new(download_path, quarantiner.stderr) | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-18 16:03:10 -07:00
										 |  |  |     def self.propagate(from: nil, to: nil) | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |       return if from.nil? || to.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       raise CaskError, "#{from} was not quarantined properly." unless detect(from) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       odebug "Propagating quarantine from #{from} to #{to}" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-08 20:20:25 +00:00
										 |  |  |       quarantine_status = toggle_no_translocation_bit(status(from)) | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-15 18:32:37 +02:00
										 |  |  |       resolved_paths = Pathname.glob(to/"**/*", File::FNM_DOTMATCH).reject(&:symlink?) | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-08 01:39:52 +00:00
										 |  |  |       system_command!("/usr/bin/xargs", | 
					
						
							| 
									
										
										
										
											2018-11-02 17:18:07 +00:00
										 |  |  |                       args:  [ | 
					
						
							| 
									
										
										
										
											2018-10-08 01:39:52 +00:00
										 |  |  |                         "-0", | 
					
						
							|  |  |  |                         "--", | 
					
						
							|  |  |  |                         "/bin/chmod", | 
					
						
							|  |  |  |                         "-h", | 
					
						
							|  |  |  |                         "u+w", | 
					
						
							|  |  |  |                       ], | 
					
						
							| 
									
										
										
										
											2018-10-08 18:23:21 +00:00
										 |  |  |                       input: resolved_paths.join("\0")) | 
					
						
							| 
									
										
										
										
											2018-10-08 01:39:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |       quarantiner = system_command("/usr/bin/xargs", | 
					
						
							| 
									
										
										
										
											2018-11-02 17:18:07 +00:00
										 |  |  |                                    args:         [ | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |                                      "-0", | 
					
						
							|  |  |  |                                      "--", | 
					
						
							| 
									
										
										
										
											2018-09-26 20:55:54 +00:00
										 |  |  |                                      xattr, | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |                                      "-w", | 
					
						
							|  |  |  |                                      QUARANTINE_ATTRIBUTE, | 
					
						
							|  |  |  |                                      quarantine_status, | 
					
						
							|  |  |  |                                    ], | 
					
						
							| 
									
										
										
										
											2018-11-02 17:18:07 +00:00
										 |  |  |                                    input:        resolved_paths.join("\0"), | 
					
						
							| 
									
										
										
										
											2018-09-04 21:11:29 +00:00
										 |  |  |                                    print_stderr: false) | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return if quarantiner.success? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       raise CaskQuarantinePropagationError.new(to, quarantiner.stderr) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2023-04-03 20:21:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-06 10:03:45 -04:00
										 |  |  |     sig { params(from: Pathname, to: Pathname, command: T.class_of(SystemCommand)).void } | 
					
						
							|  |  |  |     def self.copy_xattrs(from, to, command:) | 
					
						
							| 
									
										
										
										
											2023-04-03 20:21:37 -05:00
										 |  |  |       odebug "Copying xattrs from #{from} to #{to}" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-06 10:03:45 -04:00
										 |  |  |       command.run!( | 
					
						
							| 
									
										
										
										
											2023-05-03 11:29:01 -05:00
										 |  |  |         swift, | 
					
						
							|  |  |  |         args: [ | 
					
						
							|  |  |  |           *swift_target_args, | 
					
						
							|  |  |  |           COPY_XATTRS_SCRIPT, | 
					
						
							|  |  |  |           from, | 
					
						
							|  |  |  |           to, | 
					
						
							|  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2023-05-24 23:35:21 +02:00
										 |  |  |         sudo: !to.writable?, | 
					
						
							| 
									
										
										
										
											2023-05-03 11:29:01 -05:00
										 |  |  |       ) | 
					
						
							| 
									
										
										
										
											2023-04-03 20:21:37 -05:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2023-05-24 21:53:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Ensures that Homebrew has permission to update apps on macOS Ventura. | 
					
						
							|  |  |  |     # This may be granted either through the App Management toggle or the Full Disk Access toggle. | 
					
						
							|  |  |  |     # The system will only show a prompt for App Management, so we ask the user to grant that. | 
					
						
							| 
									
										
										
										
											2023-05-26 11:13:23 -05:00
										 |  |  |     sig { params(app: Pathname, command: T.class_of(SystemCommand)).returns(T::Boolean) } | 
					
						
							|  |  |  |     def self.app_management_permissions_granted?(app:, command:) | 
					
						
							|  |  |  |       return true unless app.directory? | 
					
						
							| 
									
										
										
										
											2023-05-24 21:53:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |       # To get macOS to prompt the user for permissions, we need to actually attempt to | 
					
						
							|  |  |  |       # modify a file in the app. | 
					
						
							| 
									
										
										
										
											2023-05-29 08:59:35 +01:00
										 |  |  |       test_file = app/".homebrew-write-test" | 
					
						
							| 
									
										
										
										
											2023-05-24 21:53:00 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |       # We can't use app.writable? here because that conflates several access checks, | 
					
						
							|  |  |  |       # including both file ownership and whether system permissions are granted. | 
					
						
							|  |  |  |       # Here we just want to check whether sudo would be needed. | 
					
						
							|  |  |  |       looks_writable_without_sudo = if app.owned? | 
					
						
							|  |  |  |         (app.lstat.mode & 0200) != 0
 | 
					
						
							|  |  |  |       elsif app.grpowned? | 
					
						
							|  |  |  |         (app.lstat.mode & 0020) != 0
 | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         (app.lstat.mode & 0002) != 0
 | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if looks_writable_without_sudo | 
					
						
							|  |  |  |         begin | 
					
						
							|  |  |  |           File.write(test_file, "") | 
					
						
							|  |  |  |           test_file.delete | 
					
						
							| 
									
										
										
										
											2023-05-26 11:13:23 -05:00
										 |  |  |           return true | 
					
						
							| 
									
										
										
										
											2023-06-04 18:04:11 +03:00
										 |  |  |         rescue Errno::EACCES, Errno::EPERM | 
					
						
							| 
									
										
										
										
											2023-05-24 21:53:00 -05:00
										 |  |  |           # Using error handler below | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         begin | 
					
						
							|  |  |  |           command.run!( | 
					
						
							|  |  |  |             "touch", | 
					
						
							|  |  |  |             args:         [ | 
					
						
							|  |  |  |               test_file, | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |             print_stderr: false, | 
					
						
							|  |  |  |             sudo:         true, | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |           command.run!( | 
					
						
							|  |  |  |             "rm", | 
					
						
							|  |  |  |             args:         [ | 
					
						
							|  |  |  |               test_file, | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |             print_stderr: false, | 
					
						
							|  |  |  |             sudo:         true, | 
					
						
							|  |  |  |           ) | 
					
						
							| 
									
										
										
										
											2023-05-26 11:13:23 -05:00
										 |  |  |           return true | 
					
						
							| 
									
										
										
										
											2023-05-24 21:53:00 -05:00
										 |  |  |         rescue ErrorDuringExecution => e | 
					
						
							|  |  |  |           # We only want to handle "touch" errors here; propagate "sudo" errors up | 
					
						
							|  |  |  |           raise e unless e.stderr.include?("touch: #{test_file}: Operation not permitted") | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-26 11:13:23 -05:00
										 |  |  |       opoo <<~EOF | 
					
						
							|  |  |  |         Your terminal does not have App Management permissions, so Homebrew will delete and reinstall the app. | 
					
						
							|  |  |  |         This may result in some configurations (like notification settings or location in the Dock/Launchpad) being lost. | 
					
						
							| 
									
										
										
										
											2023-07-06 14:10:13 +01:00
										 |  |  |         To fix this, go to System Settings > Privacy & Security > App Management and add or enable your terminal. | 
					
						
							| 
									
										
										
										
											2023-05-24 21:53:00 -05:00
										 |  |  |       EOF | 
					
						
							| 
									
										
										
										
											2023-05-29 08:59:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-26 11:13:23 -05:00
										 |  |  |       false | 
					
						
							| 
									
										
										
										
											2023-05-24 21:53:00 -05:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2018-08-31 13:16:11 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | end |