summaryrefslogtreecommitdiff
path: root/lib/nickserver/hkp/v_index_response.rb
blob: 865d4769ade25a904eb9dfe32fae5a4da78cbb3d (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
require 'nickserver/hkp'
require 'nickserver/hkp/key_info'

#
# Simple parser for Hkp KeyInfo responses.
#
# Focus is on simple here. Trying to avoid state and sideeffects.
# Parsing a response with 12 keys and validating them takes 2ms.
# So no need for memoization and making things more complex.
module Nickserver::Hkp
  class VIndexResponse

    # for this regexp to work, the source text must end in a trailing "\n",
    # which the output of sks does.
    MATCH_PUB_KEY = /(^pub:.+?\n(^uid:.+?\n)+)/m

    #  hkp_response -- raw output from a vindex hkp query (machine readable)
    def initialize(nick, hkp_response)
      @nick = nick.to_s
      @vindex_result = hkp_response[:body]
    end

    def status
      if keys.empty?
        error_status
      else
        200
      end
    end

    def keys
      key_infos.reject { |key| error_for_key(key) }
    end

    def msg
      if errors.any?
        error_messages.join "\n"
      else
        "Could not fetch keyinfo."
      end
    end

    protected

    attr_reader :vindex_result, :nick

    def error_status
      if errors.any?
        500
      else
        404
      end
    end

    def errors
      key_infos.map{|key| error_for_key(key) }.compact
    end

    def error_messages
      key_infos.map do |key|
        err = error_for_key(key)
        error_message(key, err)
      end.compact
    end

    def key_infos
      all_key_infos.select do |key_info|
        key_info.uids.include?(nick)
      end
    end

    def all_key_infos
      @all_key_infos ||= vindex_result.scan(MATCH_PUB_KEY).map do |match|
        KeyInfo.new(match[0])
      end
    end

    def error_message(key, err)
      "Ignoring key #{key.keyid} for #{nick}: #{err}" if err
    end

    def error_for_key(key)
      if key.keylen < 2048
        "key length is too short."
      elsif key.expired?
        "key expired."
      elsif key.revoked?
        "key revoked."
      elsif key.disabled?
        "key disabled."
      elsif key.expirationdate && key.expirationdate < Time.now
        "key expired"
      end
    end
  end
end