Class: Netrc

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

Defined Under Namespace

Classes: Entry, Error

Constant Summary collapse

VERSION =
"0.10.1"
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.



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

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.



171
172
173
# File 'lib/netrc.rb', line 171

def new_item_prefix
  @new_item_prefix
end

Class Method Details

.check_permissions(path) ⇒ Object



43
44
45
46
47
48
# File 'lib/netrc.rb', line 43

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



34
35
36
# File 'lib/netrc.rb', line 34

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

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

Yields:



38
39
40
41
# File 'lib/netrc.rb', line 38

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

.default_pathObject



10
11
12
# File 'lib/netrc.rb', line 10

def self.default_path
  File.join(home_path, netrc_filename)
end

.home_pathObject



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/netrc.rb', line 14

def self.home_path
  return Dir.home if defined? Dir.home # Ruby 1.9+
  if WINDOWS && !CYGWIN
    home = ENV['HOME']
    home ||= ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
    home ||= ENV['USERPROFILE']
    home.gsub("\\","/")
  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".
    (ENV['HOME'] && File.exist?(ENV['HOME']) && File.stat(ENV['HOME']).readable?) ? ENV['HOME'] : './'
  end
rescue ArgumentError
  return Dir.pwd
end

.lex(lines) ⇒ Object



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

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

.netrc_filenameObject



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

def self.netrc_filename
  WINDOWS && !CYGWIN ? "_netrc" : ".netrc"
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.



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

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.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/netrc.rb', line 52

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)


96
97
98
# File 'lib/netrc.rb', line 96

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

Instance Method Details

#[](k) ⇒ Object



173
174
175
176
177
178
179
# File 'lib/netrc.rb', line 173

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



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

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



193
194
195
196
197
198
199
200
201
202
# File 'lib/netrc.rb', line 193

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



204
205
206
# File 'lib/netrc.rb', line 204

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

#lengthObject



189
190
191
# File 'lib/netrc.rb', line 189

def length
  @data.length
end

#new_item(m, l, p) ⇒ Object



208
209
210
# File 'lib/netrc.rb', line 208

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

#saveObject



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

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



226
227
228
229
230
231
232
233
234
235
# File 'lib/netrc.rb', line 226

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