Class: Netrc

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

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =
"0.7.7"
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


124
125
126
127
128
# File 'lib/netrc.rb', line 124

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

Instance Attribute Details

#new_item_prefixObject

Returns the value of attribute new_item_prefix


130
131
132
# File 'lib/netrc.rb', line 130

def new_item_prefix
  @new_item_prefix
end

Class Method Details

.check_permissions(path) ⇒ Object


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

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

.default_pathObject


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

def self.default_path
  if WINDOWS && !CYGWIN
    File.join(ENV['USERPROFILE'].gsub("\\","/"), "_netrc")
  else
    File.join((ENV["HOME"] || "./"), ".netrc")
  end
end

.lex(lines) ⇒ Object


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

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.


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

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"}
  while ts.length > 0
    cur << ts.take + ts.readto{|t| ! skip?(t)}
    cur << ts.take
    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"}
    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.


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/netrc.rb', line 27

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


71
72
73
# File 'lib/netrc.rb', line 71

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

Instance Method Details

#[](k) ⇒ Object


132
133
134
135
136
# File 'lib/netrc.rb', line 132

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

#[]=(k, info) ⇒ Object


138
139
140
141
142
143
144
# File 'lib/netrc.rb', line 138

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


150
151
152
153
154
155
156
157
158
159
# File 'lib/netrc.rb', line 150

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


161
162
163
# File 'lib/netrc.rb', line 161

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

#lengthObject


146
147
148
# File 'lib/netrc.rb', line 146

def length
  @data.length
end

#new_item(m, l, p) ⇒ Object


165
166
167
# File 'lib/netrc.rb', line 165

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

#saveObject


169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/netrc.rb', line 169

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


183
184
185
186
187
188
189
190
191
192
# File 'lib/netrc.rb', line 183

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