virginity 0.3.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/lib/virginity.rb +6 -0
  3. data/lib/virginity/api_extensions.rb +87 -0
  4. data/lib/virginity/api_extensions/fields_to_json.rb +82 -0
  5. data/lib/virginity/api_extensions/fields_to_xml.rb +151 -0
  6. data/lib/virginity/bnf.rb +84 -0
  7. data/lib/virginity/dir_info.rb +93 -0
  8. data/lib/virginity/dir_info/content_line.rb +146 -0
  9. data/lib/virginity/dir_info/line_folding.rb +60 -0
  10. data/lib/virginity/dir_info/param.rb +208 -0
  11. data/lib/virginity/dir_info/query.rb +144 -0
  12. data/lib/virginity/encoding_decoding.rb +177 -0
  13. data/lib/virginity/encodings.rb +36 -0
  14. data/lib/virginity/fixes.rb +230 -0
  15. data/lib/virginity/vcard.rb +244 -0
  16. data/lib/virginity/vcard/base_field.rb +126 -0
  17. data/lib/virginity/vcard/categories.rb +57 -0
  18. data/lib/virginity/vcard/cleaning.rb +364 -0
  19. data/lib/virginity/vcard/field.rb +22 -0
  20. data/lib/virginity/vcard/field/params.rb +93 -0
  21. data/lib/virginity/vcard/field_values.rb +10 -0
  22. data/lib/virginity/vcard/field_values/binary.rb +22 -0
  23. data/lib/virginity/vcard/field_values/boolean.rb +14 -0
  24. data/lib/virginity/vcard/field_values/case_insensitive_value.rb +13 -0
  25. data/lib/virginity/vcard/field_values/date.rb +16 -0
  26. data/lib/virginity/vcard/field_values/integer.rb +15 -0
  27. data/lib/virginity/vcard/field_values/optional_structured_text.rb +35 -0
  28. data/lib/virginity/vcard/field_values/separated_text.rb +59 -0
  29. data/lib/virginity/vcard/field_values/structured_text.rb +71 -0
  30. data/lib/virginity/vcard/field_values/text.rb +23 -0
  31. data/lib/virginity/vcard/field_values/uri.rb +15 -0
  32. data/lib/virginity/vcard/fields.rb +284 -0
  33. data/lib/virginity/vcard/fields_osx.rb +95 -0
  34. data/lib/virginity/vcard/fields_soocial.rb +45 -0
  35. data/lib/virginity/vcard/name_handler.rb +151 -0
  36. data/lib/virginity/vcard/patching.rb +262 -0
  37. data/lib/virginity/vcard21.rb +2 -0
  38. data/lib/virginity/vcard21/base.rb +30 -0
  39. data/lib/virginity/vcard21/parser.rb +359 -0
  40. data/lib/virginity/vcard21/reader.rb +103 -0
  41. data/lib/virginity/vcard21/writer.rb +139 -0
  42. metadata +111 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5ded8c5c157bd494a6ea4644503ec864cdc4e32
4
+ data.tar.gz: 3b6e0534423dda14a021437c9ca323ef47dd5814
5
+ SHA512:
6
+ metadata.gz: f4b0415cde8eac73ea8af7c6b67fbbcec313a034a0b895a00df27e5b5516bdbf2e0b721a89b559461c6100395dc968464e1e4b4bee89bb3b66653dd0b96f226d
7
+ data.tar.gz: 3fe5472499124c8bd04e6647e91d86ef29d8152e9f47f301c8fa622be9c5f07fae01e87be91e891e67c79dda9d25b84ab75f0fdbf1881c3901d848d5b56314cd
data/lib/virginity.rb ADDED
@@ -0,0 +1,6 @@
1
+ # V stands for virginity according to http://www.urbandictionary.com/define.php?term=V%20card
2
+
3
+ require 'active_support/core_ext/string/multibyte.rb'
4
+ require 'virginity/vcard'
5
+ require 'virginity/fixes'
6
+ require 'virginity/vcard/patching'
@@ -0,0 +1,87 @@
1
+ require 'virginity'
2
+ require 'digest/sha1'
3
+
4
+
5
+ # FIXME move this to the API
6
+ module Virginity
7
+ class ContentLine
8
+
9
+ # api_id, a SHA1 hash of the whole line
10
+ def api_id
11
+ Digest::SHA1.hexdigest(to_s)
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+ require 'virginity/api_extensions/fields_to_xml'
18
+
19
+
20
+ module Virginity
21
+ class Param
22
+ def as_json(options = {})
23
+ { key => value }
24
+ end
25
+ end
26
+
27
+ module StructuredTextAsJson
28
+ def as_json(options = {})
29
+ hash = {}
30
+ components.each do |c|
31
+ hash[c.to_s] = send(c)
32
+ end
33
+ field_as_json(hash, options)
34
+ end
35
+ end
36
+
37
+ module SeparatedTextAsJson
38
+ def as_json(options = {})
39
+ field_as_json({ :values => [values] }, options)
40
+ end
41
+ end
42
+
43
+ module Values
44
+ module Binary
45
+ def as_json(options = {})
46
+ field_as_json({ :binary => binary }, options)
47
+ end
48
+ end
49
+ end
50
+
51
+ class BaseField < ContentLine
52
+ def group_and_params_as_json(options = {})
53
+ { :group => group, :params => params }
54
+ end
55
+
56
+ def field_as_json(hash, options = {})
57
+ group_and_params_as_json(options).merge(hash).delete_if { |k,v| v.nil? }
58
+ end
59
+ end
60
+
61
+
62
+
63
+ class Email < BaseField
64
+ def as_json(options = {})
65
+ field_as_json({ :address => address }, options)
66
+ end
67
+ end
68
+
69
+ class Tel < BaseField
70
+ def as_json(options = {})
71
+ field_as_json({ :number => number }, options)
72
+ end
73
+ end
74
+
75
+ class Adr < BaseField
76
+ include StructuredTextAsJson
77
+ end
78
+
79
+ class Name < BaseField
80
+ include StructuredTextAsJson
81
+ end
82
+
83
+ class Separated < BaseField
84
+ include SeparatedTextAsJson
85
+ end
86
+
87
+ end
@@ -0,0 +1,82 @@
1
+ module Virginity
2
+
3
+ class Email < BaseField
4
+ def as_json(options = {})
5
+ { :email => {
6
+ :id => api_id,
7
+ :params => params,
8
+ :address => address
9
+ }}
10
+ end
11
+ end
12
+
13
+
14
+ class Tel < BaseField
15
+ def as_json(options = {})
16
+ { :tel => {
17
+ :id => api_id,
18
+ :params => params,
19
+ :number => number
20
+ }}
21
+ end
22
+ end
23
+
24
+
25
+ class Url < BaseField
26
+ def as_json(options = {})
27
+ { :url => {
28
+ :id => api_id,
29
+ :text => text
30
+ }}
31
+ end
32
+ end
33
+
34
+
35
+ class Adr < BaseField
36
+ def as_json(options = {})
37
+ hash = { :adr => {
38
+ :id => api_id,
39
+ :params => params,
40
+ }}
41
+ components.each do |component|
42
+ hash[component] = send(component))
43
+ end
44
+ hash
45
+ end
46
+ end
47
+
48
+
49
+ class Org < BaseField
50
+ def as_json(options = {})
51
+ { :org => {
52
+ :id => api_id,
53
+ :name => orgname,
54
+ :unit1 => unit1,
55
+ :unit2 => unit2
56
+ }}
57
+ end
58
+ end
59
+
60
+
61
+ class Impp < BaseField
62
+ def as_json(options = {})
63
+ { :impp => {
64
+ :id => api_id,
65
+ :scheme => scheme,
66
+ :address => address,
67
+ :text => text
68
+ }}
69
+ end
70
+ end
71
+
72
+
73
+ class Note < BaseField
74
+ def as_json(options = {})
75
+ { :note => {
76
+ :id => api_id,
77
+ :text => text
78
+ }}
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,151 @@
1
+ module Virginity
2
+
3
+
4
+ class BaseField < ContentLine
5
+ def params_to_xml!(params, builder)
6
+ builder.params(:type => "array") do
7
+ params.each do |p|
8
+ builder.tag!(p.key, p.value, :type => "string")
9
+ end
10
+ end
11
+ end
12
+
13
+ def extra_fields_to_xml(fields, builder)
14
+ fields.each_pair { |k,v| builder.tag!(k, v) } unless fields.nil?
15
+ end
16
+ end
17
+
18
+
19
+ class BaseField < ContentLine
20
+ def params_to_xml
21
+ s = ""
22
+ unless params.empty?
23
+ s << "<params>"
24
+ params.each do |p|
25
+ s << xml_element(p.key, p.value)
26
+ end
27
+ s << "</params>"
28
+ end
29
+ s
30
+ end
31
+
32
+ # def params_to_xml
33
+ # return "" if params.empty?
34
+ # "<params>#{params.map {|p| xml_element(p.key, p.value) }.join}</params>"
35
+ # end
36
+
37
+ def value_to_xml
38
+ xml_element(@value, @value.strip)
39
+ end
40
+
41
+ def to_xml
42
+ s = "<#{name.downcase}>"
43
+ s << xml_element("group", group) unless group.nil?
44
+ s << params_to_xml
45
+ s << value_to_xml
46
+ s << "</#{name.downcase}>"
47
+ end
48
+ end
49
+
50
+
51
+ class Email < BaseField
52
+ def to_xml(options = {})
53
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
54
+ xml.email(:index => api_id ) do
55
+ xml.id api_id, :type => "string"
56
+ # params_to_xml!(params, xml)
57
+ xml.address address
58
+ extra_fields_to_xml(options[:include], xml)
59
+ end
60
+ xml.target!
61
+ end
62
+ end
63
+
64
+
65
+ class Tel < BaseField
66
+ def to_xml(options = {})
67
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
68
+ xml.telephone(:index => api_id ) do
69
+ xml.id api_id, :type => "string"
70
+ # params_to_xml!(params, xml)
71
+ xml.number number
72
+ extra_fields_to_xml(options[:include], xml)
73
+ end
74
+ xml.target!
75
+ end
76
+ end
77
+
78
+
79
+ class Url < BaseField
80
+ def to_xml(options = {})
81
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
82
+ xml.url(:index => api_id) do
83
+ xml.id api_id, :type => "string"
84
+ xml.text text
85
+ extra_fields_to_xml(options[:include], xml)
86
+ end
87
+ xml.target!
88
+ end
89
+ end
90
+
91
+
92
+ class Adr < BaseField
93
+ def to_xml(options = {})
94
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
95
+ xml.address(:index => api_id) do
96
+ xml.id api_id, :type => "string"
97
+ # params_to_xml!(params, xml)
98
+ components.each do |component|
99
+ xml.tag!(component, send(component))
100
+ end
101
+ extra_fields_to_xml(options[:include], xml)
102
+ end
103
+ xml.target!
104
+ end
105
+ end
106
+
107
+
108
+ class Org < BaseField
109
+ def to_xml(options = {})
110
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
111
+ xml.organisation :index => api_id do
112
+ xml.id api_id, :type => "string"
113
+ xml.name orgname
114
+ xml.unit1 unit1
115
+ xml.unit2 unit2
116
+ extra_fields_to_xml(options[:include], xml)
117
+ end
118
+ xml.target!
119
+ end
120
+ end
121
+
122
+
123
+ class Impp < BaseField
124
+ def to_xml(options = {})
125
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
126
+ xml.impp(:index => api_id ) do
127
+ xml.id api_id, :type => "string"
128
+ xml.scheme scheme
129
+ xml.address address
130
+ xml.value text
131
+ extra_fields_to_xml(options[:include], xml)
132
+ end
133
+ xml.target!
134
+ end
135
+ end
136
+
137
+
138
+ class TextField < BaseField
139
+ def to_xml(options = {})
140
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
141
+ xml.note(:index => api_id) do
142
+ xml.id api_id, :type => "string"
143
+ xml.text text
144
+ extra_fields_to_xml(options[:include], xml)
145
+ end
146
+ xml.target!
147
+ end
148
+ end
149
+
150
+
151
+ end
@@ -0,0 +1,84 @@
1
+ module Virginity
2
+
3
+ module Rfc882
4
+ # rfc822 has a slightly different definition of quoted string compared to rfc2425... it's better
5
+ QTEXT = /[^\"\\\n]/ # <any CHAR excepting <"> "\" & CR, and including linear-white-space
6
+ QUOTED_PAIR = /\\./
7
+ QUOTED_STRING = /(\"((#{QTEXT}|#{QUOTED_PAIR})*)\")/
8
+ end
9
+
10
+
11
+ module Rfc2234 #:nodoc: # Augmented BNF for Syntax Specifications: ABNF
12
+ CRLF = /\r\n/
13
+ DIGIT = /\d/
14
+ WSP = /\s/ # whitespace
15
+ end
16
+
17
+ module Rfc2425
18
+ end
19
+
20
+ # Contains regular expression strings for the EBNF of rfc 2425.
21
+ module Bnf #:nodoc:
22
+ include Rfc882
23
+ include Rfc2234
24
+ include Rfc2425
25
+ # 1*(ALPHA / DIGIT / "-")
26
+ # added underscore '_' because it's produced by Notes - X-LOTUS-CHILD_UID
27
+ # added a slash '/' so that it will match lines like: "X-messaging/xmpp-All:someone@gmail.com"
28
+ # added a space ' ' so that it will match lines like: "X-GOOGLE TALK;TYPE=WORK:janklaassen"
29
+ NAME = '[a-zA-Z0-9][a-zA-Z0-9\-\_\/\ ]*'
30
+
31
+ # <"> <Any character except CTLs, DQUOTE> <">
32
+ # CTL = <any ASCII control ; ( 0- 37, 0.- 31.)
33
+ # character and DEL> ; ( 177, 127.)
34
+ # DQOUTE = '"'
35
+ # QSTR = '"([^"]*)"'
36
+ QSTR = QUOTED_STRING # use the definition from rfc822
37
+
38
+ # *<Any character except CTLs, DQUOTE, ";", ":", ",">
39
+ PTEXT = '([^";:,]+)'
40
+
41
+ # param-value = ptext / quoted-string
42
+ PVALUE = "(?:#{QSTR}|#{PTEXT})"
43
+
44
+ # param = name "=" param-value *("," param-value)
45
+ PARAM = ";(#{NAME})=((?:#{PVALUE})?(?:,#{PVALUE})*)"
46
+
47
+ # V3.0: contentline = [group "."] name *(";" param) ":" value
48
+ # V2.1: contentline = *( group "." ) name *(";" param) ":" value
49
+ #
50
+ #LINE = "((?:#{NAME}\\.)*)?(#{NAME})([^:]*)\:(.*)"
51
+ # tcmalloc (used by ree) having memory issues with that one:
52
+ #LINE = "^((?:#{NAME}\\.)*)?(#{NAME})((?:#{PARAM})*):(.*)$"
53
+ #LINE = "^((?:#{NAME}\\.)*)?(#{NAME})((?:#{PARAM})*):"
54
+ # We do not accept the V2.1 syntax.
55
+ LINE = "^(#{NAME}\\.)?(#{NAME})((?:#{PARAM})*):"
56
+
57
+ # date = date-fullyear ["-"] date-month ["-"] date-mday
58
+ # date-fullyear = 4 DIGIT
59
+ # date-month = 2 DIGIT
60
+ # date-mday = 2 DIGIT
61
+ DATE = '(\d\d\d\d)-?(\d\d)-?(\d\d)'
62
+
63
+ # time = time-hour [":"] time-minute [":"] time-second [time-secfrac] [time-zone]
64
+ # time-hour = 2 DIGIT
65
+ # time-minute = 2 DIGIT
66
+ # time-second = 2 DIGIT
67
+ # time-secfrac = "," 1*DIGIT
68
+ # time-zone = "Z" / time-numzone
69
+ # time-numzome = sign time-hour [":"] time-minute
70
+ TIME = '(\d\d):?(\d\d):?(\d\d)(\.\d+)?(Z|[-+]\d\d:?\d\d)?'
71
+
72
+ # integer = (["+"] / "-") 1*DIGIT
73
+ INTEGER = '[-+]?\d+'
74
+
75
+ # QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
76
+ # ; Any character except CTLs and DQUOTE
77
+ QSAFECHAR = '[ \t\x21\x23-\x7e\x80-\xff]'
78
+
79
+ # SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII
80
+ # ; Any character except CTLs, DQUOTE, ";", ":", ","
81
+ SAFECHAR = '[ \t\x21\x23-\x2b\x2d-\x39\x3c-\x7e\x80-\xff]'
82
+ end
83
+
84
+ end
@@ -0,0 +1,93 @@
1
+ require 'virginity/bnf'
2
+ require 'virginity/dir_info/line_folding'
3
+ require 'virginity/dir_info/content_line'
4
+ require 'virginity/dir_info/query'
5
+
6
+ require 'virginity/vcard21/reader'
7
+
8
+ $KCODE = 'U' unless defined? Encoding::UTF_8
9
+
10
+ module Virginity
11
+
12
+ # see rfc 2425, MIME Content-Type for Directory Information.
13
+ #
14
+ # Basically a DirectoryInformation-object is a collection of lines (see ContentLine)
15
+ class DirectoryInformation
16
+ include Query
17
+ extend Virginity::Vcard21::Reader
18
+ extend Encodings
19
+ attr_reader :lines
20
+
21
+ # decode directory information text
22
+ # TODO accept an array of lines as the argument, make a special from_string(string="")
23
+ def initialize(string = "")
24
+ raise "expected a string but found #{string.inspect}, a #{string.class}" unless string.is_a? String
25
+ @lines = LineFolding::unfold_and_split(string).map do |line|
26
+ ContentLine.parse(line)
27
+ end
28
+ end
29
+
30
+ # string representation
31
+ def encode
32
+ LineFolding::fold(unfolded)
33
+ end
34
+ alias_method :to_s, :encode
35
+
36
+ # replace _all_ lines
37
+ def lines=(replace_lines)
38
+ @lines = replace_lines.to_a.map { |line| ContentLine.parse(line.to_s) }
39
+ end
40
+
41
+ def inspect
42
+ "#<#{self.class}:#{object_id}>"
43
+ end
44
+
45
+ def delete(*lines_to_delete)
46
+ lines_to_delete.compact.map { |line| lines.delete(line) }.compact
47
+ end
48
+
49
+ # remove only this exact content line identified by its object-id
50
+ def delete_content_line(cl)
51
+ lines.delete_if { |line| line.object_id == cl.object_id }
52
+ end
53
+
54
+ # append a line
55
+ def <<(line)
56
+ lines << ContentLine.parse(line.to_s)
57
+ end
58
+ alias_method :push, :<<
59
+
60
+ # string representation that is not folded (see LineFolding)
61
+ LF = "\n"
62
+ def unfolded
63
+ @lines.join(LF) << LF
64
+ end
65
+
66
+ def pretty_print(q)
67
+ q.text unfolded
68
+ end
69
+
70
+ # equallity is defined as having the same lines
71
+ def ==(other)
72
+ return false if other.class != self.class
73
+ # checking for lines.size is an optimisation
74
+ @lines.size == other.lines.size and @lines.sort == other.lines.sort
75
+ end
76
+
77
+ # eql? is defined as being of the same class (not a descendent class like Vcard) and having the same lines
78
+ def eql?(other)
79
+ self.class == other.class and self == other
80
+ end
81
+
82
+ # are all @lines also present in other?
83
+ def subset_of?(other)
84
+ @lines.all? do |line|
85
+ other.first_match(line.to_s)
86
+ end
87
+ end
88
+
89
+ def superset_of?(other)
90
+ other.subset_of?(self)
91
+ end
92
+ end
93
+ end