Class: Netrc

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

Defined Under Namespace

Classes: Error

Constant Summary collapse

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



116
117
118
119
120
# File 'lib/netrc.rb', line 116

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.



122
123
124
# File 'lib/netrc.rb', line 122

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



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

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 7-tuple, containing:

  • 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.



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

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
    cur << ts.readto{|t| t == "login"} + ts.take + ts.readto{|t| ! skip?(t)}
    cur << ts.take
    cur << ts.readto{|t| t == "password"} + ts.take + ts.readto{|t| ! skip?(t)}
    cur << ts.take
    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
# File 'lib/netrc.rb', line 27

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

.skip?(s) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/netrc.rb', line 67

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

Instance Method Details

#[](k) ⇒ Object



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

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

#[]=(k, info) ⇒ Object



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

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



142
143
144
145
146
147
148
149
150
151
# File 'lib/netrc.rb', line 142

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



153
154
155
# File 'lib/netrc.rb', line 153

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

#lengthObject



138
139
140
# File 'lib/netrc.rb', line 138

def length
  @data.length
end

#new_item(m, l, p) ⇒ Object



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

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

#saveObject



161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/netrc.rb', line 161

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



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

def unparse
  @pre + @data.map(&:join).join
end