Class: Netrc

Inherits:
Object
  • Object
show all
Defined in:
lib/netrc.rb

Defined Under Namespace

Classes: Entry, Error

Constant Summary collapse

VERSION =
"0.8.0"
WINDOWS =
RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
CYGWIN =
RbConfig::CONFIG["host_os"] =~ /cygwin/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, data) ⇒ Netrc

Returns a new instance of Netrc.



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/netrc.rb', line 146

def initialize(path, data)
  @new_item_prefix = ''
  @path = path
  @pre, @data = data

  if @data && @data.last && :default == @data.last[0]
    @default = @data.pop
  else
    @default = nil
  end
end

Instance Attribute Details

#new_item_prefixObject

Returns the value of attribute new_item_prefix.



158
159
160
# File 'lib/netrc.rb', line 158

def new_item_prefix
  @new_item_prefix
end

Class Method Details

.check_permissions(path) ⇒ Object



30
31
32
33
34
35
# File 'lib/netrc.rb', line 30

def self.check_permissions(path)
  perm = File.stat(path).mode & 0777
  if perm != 0600 && !(WINDOWS) && !(Netrc.config[:allow_permissive_netrc_file])
    raise Error, "Permission bits for '#{path}' should be 0600, but are "+perm.to_s(8)
  end
end

.configObject



21
22
23
# File 'lib/netrc.rb', line 21

def self.config
  @config ||= {}
end

.configure {|self.config| ... } ⇒ Object

Yields:



25
26
27
28
# File 'lib/netrc.rb', line 25

def self.configure
  yield(self.config) if block_given?
  self.config
end

.default_pathObject



10
11
12
13
14
15
16
17
18
19
# File 'lib/netrc.rb', line 10

def self.default_path
  if WINDOWS && !CYGWIN
    File.join(ENV['USERPROFILE'].gsub("\\","/"), "_netrc")
  else
    # In some cases, people run master process as "root" user, and run worker processes as "www" user.
    # Fix "Permission denied" error in worker processes when $HOME is "/root".
    default_dir = (ENV['HOME'] && File.exists?(ENV['HOME']) && File.stat(ENV['HOME']).readable?) ? ENV['HOME'] : './'
    File.join(default_dir, ".netrc")
  end
end

.lex(lines) ⇒ Object



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
# File 'lib/netrc.rb', line 56

def self.lex(lines)
  tokens = []
  for line in lines
    content, comment = line.split(/(\s*#.*)/m)
    content.each_char do |char|
      case char
      when /\s/
        if tokens.last && tokens.last[-1..-1] =~ /\s/
          tokens.last << char
        else
          tokens << char
        end
      else
        if tokens.last && tokens.last[-1..-1] =~ /\S/
          tokens.last << char
        else
          tokens << char
        end
      end
    end
    if comment
      tokens << comment
    end
  end
  tokens
end

.parse(ts) ⇒ Object

Returns two values, a header and a list of items. Each item is a tuple, containing some or all of:

  • machine keyword (including trailing whitespace+comments)

  • machine name

  • login keyword (including surrounding whitespace+comments)

  • login

  • password keyword (including surrounding whitespace+comments)

  • password

  • trailing chars

This lets us change individual fields, then write out the file with all its original formatting.



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
# File 'lib/netrc.rb', line 98

def self.parse(ts)
  cur, item = [], []

  def ts.take
    if length < 1
      raise Error, "unexpected EOF"
    end
    shift
  end

  def ts.readto
    l = []
    while length > 0 && ! yield(self[0])
      l << shift
    end
    return l.join
  end

  pre = ts.readto{|t| t == "machine" || t == "default"}

  while ts.length > 0
    if ts[0] == 'default'
      cur << ts.take.to_sym
      cur << ''
    else
      cur << ts.take + ts.readto{|t| ! skip?(t)}
      cur << ts.take
    end

    if ts.include?('login')
      cur << ts.readto{|t| t == "login"} + ts.take + ts.readto{|t| ! skip?(t)}
      cur << ts.take
    end

    if ts.include?('password')
      cur << ts.readto{|t| t == "password"} + ts.take + ts.readto{|t| ! skip?(t)}
      cur << ts.take
    end

    cur << ts.readto{|t| t == "machine" || t == "default"}

    item << cur
    cur = []
  end

  [pre, item]
end

.read(path = default_path) ⇒ Object

Reads path and parses it as a .netrc file. If path doesn’t exist, returns an empty object. Decrypt paths ending in .gpg.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/netrc.rb', line 39

def self.read(path=default_path)
  check_permissions(path)
  data = if path =~ /\.gpg$/
    decrypted = `gpg --batch --quiet --decrypt #{path}`
    if $?.success?
      decrypted
    else
      raise Error.new("Decrypting #{path} failed.") unless $?.success?
    end
  else
    File.read(path)
  end
  new(path, parse(lex(data.lines.to_a)))
rescue Errno::ENOENT
  new(path, parse(lex([])))
end

.skip?(s) ⇒ Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/netrc.rb', line 83

def self.skip?(s)
  s =~ /^\s/
end

Instance Method Details

#[](k) ⇒ Object



160
161
162
163
164
165
166
# File 'lib/netrc.rb', line 160

def [](k)
  if item = @data.detect {|datum| datum[1] == k}
    Entry.new(item[3], item[5])
  elsif @default
    Entry.new(@default[3], @default[5])
  end
end

#[]=(k, info) ⇒ Object



168
169
170
171
172
173
174
# File 'lib/netrc.rb', line 168

def []=(k, info)
  if item = @data.detect {|datum| datum[1] == k}
    item[3], item[5] = info
  else
    @data << new_item(k, info[0], info[1])
  end
end

#delete(key) ⇒ Object



180
181
182
183
184
185
186
187
188
189
# File 'lib/netrc.rb', line 180

def delete(key)
  datum = nil
  for value in @data
    if value[1] == key
      datum = value
      break
    end
  end
  @data.delete(datum)
end

#each(&block) ⇒ Object



191
192
193
# File 'lib/netrc.rb', line 191

def each(&block)
  @data.each(&block)
end

#lengthObject



176
177
178
# File 'lib/netrc.rb', line 176

def length
  @data.length
end

#new_item(m, l, p) ⇒ Object



195
196
197
# File 'lib/netrc.rb', line 195

def new_item(m, l, p)
  [new_item_prefix+"machine ", m, "\n  login ", l, "\n  password ", p, "\n"]
end

#saveObject



199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/netrc.rb', line 199

def save
  if @path =~ /\.gpg$/
    e = IO.popen("gpg -a --batch --default-recipient-self -e", "r+") do |gpg|
      gpg.puts(unparse)
      gpg.close_write
      gpg.read
    end
    raise Error.new("Encrypting #{@path} failed.") unless $?.success?
    File.open(@path, 'w', 0600) {|file| file.print(e)}
  else
    File.open(@path, 'w', 0600) {|file| file.print(unparse)}
  end
end

#unparseObject



213
214
215
216
217
218
219
220
221
222
# File 'lib/netrc.rb', line 213

def unparse
  @pre + @data.map do |datum|
    datum = datum.join
    unless datum[-1..-1] == "\n"
      datum << "\n"
    else
      datum
    end
  end.join
end