summaryrefslogtreecommitdiff
path: root/lib/chef/resource/windows_font.rb
blob: d57720b704241817f29ce521a36c2edfa6fbdafa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
#
# Copyright:: 2014-2018, Schuberg Philis BV.
# Copyright:: Copyright (c) Chef Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require_relative "../resource"

class Chef
  class Resource
    class WindowsFont < Chef::Resource
      require_relative "../util/path_helper"
      unified_mode true

      provides(:windows_font) { true }

      description "Use the **windows_font** resource to install font files on Windows. By default, the font is sourced from the cookbook using the resource, but a URI source can be specified as well."
      introduced "14.0"
      examples <<~DOC
      **Install a font from a https source**:

      ```ruby
      windows_font 'Custom.otf' do
        source 'https://example.com/Custom.otf'
      end
      ```
      DOC

      property :font_name, String,
        description: "An optional property to set the name of the font to install if it differs from the resource block's name.",
        name_property: true

      property :source, String,
        description: "A local filesystem path or URI that is used to source the font file.",
        coerce: proc { |x| /^.:.*/.match?(x) ? x.tr('\\', "/").gsub("//", "/") : x }

      action :install, description: "Install a font to the system fonts directory" do
        if font_exists?
          logger.trace("Not installing font: #{new_resource.font_name} as font already installed.")
        else
          retrieve_cookbook_font
          install_font
          del_cookbook_font
        end
      end

      action_class do
        # if a source is specified fetch using remote_file. If not use cookbook_file
        def retrieve_cookbook_font
          font_file = new_resource.font_name
          if new_resource.source
            declare_resource(:remote_file, font_file) do
              action :nothing
              source source_uri
              path Chef::Util::PathHelper.join(ENV["TEMP"], font_file)
            end.run_action(:create)
          else
            declare_resource(:cookbook_file, font_file) do
              action    :nothing
              cookbook  cookbook_name.to_s unless cookbook_name.nil?
              path      Chef::Util::PathHelper.join(ENV["TEMP"], font_file)
            end.run_action(:create)
          end
        end

        # delete the temp cookbook file
        def del_cookbook_font
          file Chef::Util::PathHelper.join(ENV["TEMP"], new_resource.font_name) do
            action :delete
          end
        end

        # install the font into the appropriate fonts directory
        def install_font
          require "win32ole" if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
          fonts_dir = Chef::Util::PathHelper.join(ENV["windir"], "fonts")
          folder = WIN32OLE.new("Shell.Application").Namespace(fonts_dir)
          converge_by("install font #{new_resource.font_name} to #{fonts_dir}") do
            folder.CopyHere(Chef::Util::PathHelper.join(ENV["TEMP"], new_resource.font_name))
          end
        end

        # Check to see if the font is installed in the fonts dir
        #
        # @return [Boolean] Is the font is installed?
        def font_exists?
          require "win32ole" if RUBY_PLATFORM.match?(/mswin|mingw32|windows/)
          fonts_dir = WIN32OLE.new("WScript.Shell").SpecialFolders("Fonts")
          fonts_dir_local = Chef::Util::PathHelper.join(ENV["home"], "AppData/Local/Microsoft/Windows/fonts")
          logger.trace("Seeing if the font at #{Chef::Util::PathHelper.join(fonts_dir, new_resource.font_name)} exists")
          ::File.exist?(Chef::Util::PathHelper.join(fonts_dir, new_resource.font_name)) || ::File.exist?(Chef::Util::PathHelper.join(fonts_dir_local, new_resource.font_name))
        end

        # Parse out the schema provided to us to see if it's one we support via remote_file.
        # We do this because URI will parse C:/foo as schema 'c', which won't work with remote_file
        #
        # @return [Boolean]
        def remote_file_schema?(schema)
          return true if %w{http https ftp}.include?(schema)
        end

        # return new_resource.source if we have a proper URI specified
        # if it's a local file listed as a source return it in file:// format
        #
        # @return [String] path to the font
        def source_uri
          begin
            require "uri" unless defined?(URI)
            if remote_file_schema?(URI.parse(new_resource.source).scheme)
              logger.trace("source property starts with ftp/http. Using source property unmodified")
              return new_resource.source
            end
          rescue URI::InvalidURIError
            Chef::Log.warn("source property of #{new_resource.source} could not be processed as a URI. Check the format you provided.")
          end
          logger.trace("source property does not start with ftp/http. Prepending with file:// as it appears to be a local file.")
          "file://#{new_resource.source}"
        end
      end
    end
  end
end