tilia-vobject 4.0.0.pre.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rubocop.yml +32 -0
  4. data/.simplecov +4 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.sabre.md +626 -0
  7. data/CONTRIBUTING.md +25 -0
  8. data/Gemfile +17 -0
  9. data/Gemfile.lock +68 -0
  10. data/LICENSE +27 -0
  11. data/LICENSE.sabre +27 -0
  12. data/README.md +63 -0
  13. data/Rakefile +17 -0
  14. data/bin/vobject +7 -0
  15. data/lib/tilia/v_object/birthday_calendar_generator.rb +142 -0
  16. data/lib/tilia/v_object/cli.rb +582 -0
  17. data/lib/tilia/v_object/component/available.rb +107 -0
  18. data/lib/tilia/v_object/component/v_alarm.rb +114 -0
  19. data/lib/tilia/v_object/component/v_availability.rb +128 -0
  20. data/lib/tilia/v_object/component/v_calendar.rb +468 -0
  21. data/lib/tilia/v_object/component/v_card.rb +457 -0
  22. data/lib/tilia/v_object/component/v_event.rb +127 -0
  23. data/lib/tilia/v_object/component/v_free_busy.rb +81 -0
  24. data/lib/tilia/v_object/component/v_journal.rb +75 -0
  25. data/lib/tilia/v_object/component/v_time_zone.rb +51 -0
  26. data/lib/tilia/v_object/component/v_todo.rb +147 -0
  27. data/lib/tilia/v_object/component.rb +591 -0
  28. data/lib/tilia/v_object/date_time_parser.rb +486 -0
  29. data/lib/tilia/v_object/document.rb +218 -0
  30. data/lib/tilia/v_object/element_list.rb +18 -0
  31. data/lib/tilia/v_object/eof_exception.rb +8 -0
  32. data/lib/tilia/v_object/free_busy_data.rb +149 -0
  33. data/lib/tilia/v_object/free_busy_generator.rb +465 -0
  34. data/lib/tilia/v_object/i_tip/broker.rb +909 -0
  35. data/lib/tilia/v_object/i_tip/i_tip_exception.rb +9 -0
  36. data/lib/tilia/v_object/i_tip/message.rb +109 -0
  37. data/lib/tilia/v_object/i_tip/same_organizer_for_all_components_exception.rb +13 -0
  38. data/lib/tilia/v_object/i_tip.rb +10 -0
  39. data/lib/tilia/v_object/node.rb +192 -0
  40. data/lib/tilia/v_object/parameter.rb +327 -0
  41. data/lib/tilia/v_object/parse_exception.rb +7 -0
  42. data/lib/tilia/v_object/parser/json.rb +149 -0
  43. data/lib/tilia/v_object/parser/mime_dir.rb +543 -0
  44. data/lib/tilia/v_object/parser/parser.rb +61 -0
  45. data/lib/tilia/v_object/parser/xml/element/key_value.rb +60 -0
  46. data/lib/tilia/v_object/parser/xml/element.rb +11 -0
  47. data/lib/tilia/v_object/parser/xml.rb +322 -0
  48. data/lib/tilia/v_object/parser.rb +10 -0
  49. data/lib/tilia/v_object/property/binary.rb +96 -0
  50. data/lib/tilia/v_object/property/boolean.rb +57 -0
  51. data/lib/tilia/v_object/property/flat_text.rb +52 -0
  52. data/lib/tilia/v_object/property/float_value.rb +107 -0
  53. data/lib/tilia/v_object/property/i_calendar/cal_address.rb +49 -0
  54. data/lib/tilia/v_object/property/i_calendar/date.rb +15 -0
  55. data/lib/tilia/v_object/property/i_calendar/date_time.rb +330 -0
  56. data/lib/tilia/v_object/property/i_calendar/duration.rb +65 -0
  57. data/lib/tilia/v_object/property/i_calendar/period.rb +124 -0
  58. data/lib/tilia/v_object/property/i_calendar/recur.rb +173 -0
  59. data/lib/tilia/v_object/property/i_calendar.rb +14 -0
  60. data/lib/tilia/v_object/property/integer_value.rb +60 -0
  61. data/lib/tilia/v_object/property/text.rb +352 -0
  62. data/lib/tilia/v_object/property/time.rb +85 -0
  63. data/lib/tilia/v_object/property/unknown.rb +30 -0
  64. data/lib/tilia/v_object/property/uri.rb +78 -0
  65. data/lib/tilia/v_object/property/utc_offset.rb +56 -0
  66. data/lib/tilia/v_object/property/v_card/date.rb +31 -0
  67. data/lib/tilia/v_object/property/v_card/date_and_or_time.rb +343 -0
  68. data/lib/tilia/v_object/property/v_card/date_time.rb +22 -0
  69. data/lib/tilia/v_object/property/v_card/language_tag.rb +41 -0
  70. data/lib/tilia/v_object/property/v_card/time_stamp.rb +74 -0
  71. data/lib/tilia/v_object/property/v_card.rb +13 -0
  72. data/lib/tilia/v_object/property.rb +532 -0
  73. data/lib/tilia/v_object/reader.rb +73 -0
  74. data/lib/tilia/v_object/recur/event_iterator.rb +417 -0
  75. data/lib/tilia/v_object/recur/no_instances_exception.rb +11 -0
  76. data/lib/tilia/v_object/recur/r_date_iterator.rb +138 -0
  77. data/lib/tilia/v_object/recur/r_rule_iterator.rb +717 -0
  78. data/lib/tilia/v_object/recur.rb +10 -0
  79. data/lib/tilia/v_object/settings.rb +32 -0
  80. data/lib/tilia/v_object/splitter/i_calendar.rb +95 -0
  81. data/lib/tilia/v_object/splitter/splitter_interface.rb +31 -0
  82. data/lib/tilia/v_object/splitter/v_card.rb +56 -0
  83. data/lib/tilia/v_object/splitter.rb +9 -0
  84. data/lib/tilia/v_object/string_util.rb +58 -0
  85. data/lib/tilia/v_object/time_zone_data/exchange_zones.rb +96 -0
  86. data/lib/tilia/v_object/time_zone_data/lotus_zones.rb +104 -0
  87. data/lib/tilia/v_object/time_zone_data/php_zones.rb +49 -0
  88. data/lib/tilia/v_object/time_zone_data/windows_zones.rb +121 -0
  89. data/lib/tilia/v_object/time_zone_data.rb +10 -0
  90. data/lib/tilia/v_object/time_zone_util.rb +213 -0
  91. data/lib/tilia/v_object/uuid_util.rb +51 -0
  92. data/lib/tilia/v_object/v_card_converter.rb +354 -0
  93. data/lib/tilia/v_object/version.rb +9 -0
  94. data/lib/tilia/v_object/writer.rb +56 -0
  95. data/lib/tilia/v_object.rb +45 -0
  96. data/lib/tilia/vobject.rb +1 -0
  97. data/resources/schema/xcal.rng +1192 -0
  98. data/resources/schema/xcard.rng +388 -0
  99. data/test/test_helper.rb +56 -0
  100. data/test/v_object/attach_issue_test.rb +19 -0
  101. data/test/v_object/birthday_calendar_generator_test.rb +463 -0
  102. data/test/v_object/cli_mock.rb +19 -0
  103. data/test/v_object/cli_test.rb +460 -0
  104. data/test/v_object/component/available_test.rb +59 -0
  105. data/test/v_object/component/v_alarm_test.rb +160 -0
  106. data/test/v_object/component/v_availability_test.rb +388 -0
  107. data/test/v_object/component/v_calendar_test.rb +646 -0
  108. data/test/v_object/component/v_card_test.rb +258 -0
  109. data/test/v_object/component/v_event_test.rb +85 -0
  110. data/test/v_object/component/v_free_busy_test.rb +59 -0
  111. data/test/v_object/component/v_journal_test.rb +85 -0
  112. data/test/v_object/component/v_time_zone_test.rb +47 -0
  113. data/test/v_object/component/v_todo_test.rb +172 -0
  114. data/test/v_object/component_test.rb +419 -0
  115. data/test/v_object/date_time_parser_test.rb +526 -0
  116. data/test/v_object/document_test.rb +71 -0
  117. data/test/v_object/element_list_test.rb +27 -0
  118. data/test/v_object/em_client_test.rb +53 -0
  119. data/test/v_object/empty_parameter_test.rb +65 -0
  120. data/test/v_object/empty_value_issue_test.rb +25 -0
  121. data/test/v_object/fake_component.rb +21 -0
  122. data/test/v_object/free_busy_data_test.rb +285 -0
  123. data/test/v_object/free_busy_generator_test.rb +637 -0
  124. data/test/v_object/google_colon_escaping_test.rb +27 -0
  125. data/test/v_object/i_calendar/attach_parse_test.rb +24 -0
  126. data/test/v_object/i_tip/broker_attendee_reply_test.rb +1042 -0
  127. data/test/v_object/i_tip/broker_delete_event_test.rb +175 -0
  128. data/test/v_object/i_tip/broker_new_event_test.rb +440 -0
  129. data/test/v_object/i_tip/broker_process_message_test.rb +153 -0
  130. data/test/v_object/i_tip/broker_process_reply_test.rb +402 -0
  131. data/test/v_object/i_tip/broker_tester.rb +71 -0
  132. data/test/v_object/i_tip/broker_update_event_test.rb +763 -0
  133. data/test/v_object/i_tip/evolution_test.rb +2644 -0
  134. data/test/v_object/i_tip/message_test.rb +25 -0
  135. data/test/v_object/issue153.vcf +352 -0
  136. data/test/v_object/issue153_test.rb +12 -0
  137. data/test/v_object/issue26_test.rb +25 -0
  138. data/test/v_object/issue36_work_around_test.rb +37 -0
  139. data/test/v_object/issue40_test.rb +26 -0
  140. data/test/v_object/issue64.vcf +351 -0
  141. data/test/v_object/issue64_test.rb +17 -0
  142. data/test/v_object/issue96_test.rb +22 -0
  143. data/test/v_object/issue_undefined_index_test.rb +24 -0
  144. data/test/v_object/j_cal_test.rb +150 -0
  145. data/test/v_object/j_card_test.rb +192 -0
  146. data/test/v_object/line_folding_issue_test.rb +19 -0
  147. data/test/v_object/mock_document.rb +6 -0
  148. data/test/v_object/parameter_test.rb +109 -0
  149. data/test/v_object/parser/json_test.rb +370 -0
  150. data/test/v_object/parser/mime_dir_test.rb +14 -0
  151. data/test/v_object/parser/quoted_printable_test.rb +78 -0
  152. data/test/v_object/parser/xml_test.rb +2563 -0
  153. data/test/v_object/property/binary_test.rb +12 -0
  154. data/test/v_object/property/boolean_test.rb +18 -0
  155. data/test/v_object/property/compound_test.rb +43 -0
  156. data/test/v_object/property/float_test.rb +20 -0
  157. data/test/v_object/property/i_calendar/cal_address_test.rb +26 -0
  158. data/test/v_object/property/i_calendar/date_time_test.rb +303 -0
  159. data/test/v_object/property/i_calendar/duration_test.rb +14 -0
  160. data/test/v_object/property/i_calendar/recur_test.rb +39 -0
  161. data/test/v_object/property/text_test.rb +81 -0
  162. data/test/v_object/property/v_card/date_and_or_time_test.rb +205 -0
  163. data/test/v_object/property/v_card/language_tag_test.rb +35 -0
  164. data/test/v_object/property_test.rb +338 -0
  165. data/test/v_object/reader_test.rb +403 -0
  166. data/test/v_object/recur/event_iterator/by_month_in_daily_test.rb +52 -0
  167. data/test/v_object/recur/event_iterator/by_set_pos_hang_test.rb +55 -0
  168. data/test/v_object/recur/event_iterator/expand_floating_times_test.rb +109 -0
  169. data/test/v_object/recur/event_iterator/fifth_tuesday_problem_test.rb +45 -0
  170. data/test/v_object/recur/event_iterator/incorrect_expand_test.rb +53 -0
  171. data/test/v_object/recur/event_iterator/infinite_loop_problem_test.rb +75 -0
  172. data/test/v_object/recur/event_iterator/issue48_test.rb +43 -0
  173. data/test/v_object/recur/event_iterator/issue50_test.rb +123 -0
  174. data/test/v_object/recur/event_iterator/main_test.rb +1222 -0
  175. data/test/v_object/recur/event_iterator/missing_overridden_test.rb +55 -0
  176. data/test/v_object/recur/event_iterator/no_instances_test.rb +32 -0
  177. data/test/v_object/recur/event_iterator/override_first_event_test.rb +106 -0
  178. data/test/v_object/recur/r_date_iterator_test.rb +44 -0
  179. data/test/v_object/recur/r_rule_iterator_test.rb +608 -0
  180. data/test/v_object/recurrence_iterator/UntilRespectsTimezoneTest.ics +39 -0
  181. data/test/v_object/slash_r_test.rb +15 -0
  182. data/test/v_object/splitter/i_calendar_test.rb +299 -0
  183. data/test/v_object/splitter/v_card_test.rb +173 -0
  184. data/test/v_object/string_util_test.rb +37 -0
  185. data/test/v_object/test_case.rb +42 -0
  186. data/test/v_object/time_zone_util_test.rb +271 -0
  187. data/test/v_object/uuid_util_test.rb +18 -0
  188. data/test/v_object/v_card21_test.rb +43 -0
  189. data/test/v_object/v_card_converter_test.rb +419 -0
  190. data/test/v_object/version_test.rb +15 -0
  191. data/test/v_object/writer_test.rb +33 -0
  192. data/tilia-vobject.gemspec +17 -0
  193. metadata +308 -0
@@ -0,0 +1,173 @@
1
+ module Tilia
2
+ module VObject
3
+ class Property
4
+ module ICalendar
5
+ # Recur property.
6
+ #
7
+ # This object represents RECUR properties.
8
+ # These values are just used for RRULE and the now deprecated EXRULE.
9
+ #
10
+ # The RRULE property may look something like this:
11
+ #
12
+ # RRULE:FREQ=MONTHLY;BYDAY=1,2,3;BYHOUR=5.
13
+ #
14
+ # This property exposes this as a key=>value array that is accessible using
15
+ # getParts, and may be set using setParts.
16
+ class Recur < Property
17
+ # Updates the current value.
18
+ #
19
+ # This may be either a single, or multiple strings in an array.
20
+ #
21
+ # @param string|array value
22
+ #
23
+ # @return void
24
+ def value=(value)
25
+ # If we're getting the data from json, we'll be receiving an object
26
+ value = [value] if false # value.is_a?(Array) TODO
27
+
28
+ if value.is_a?(Hash)
29
+ new_val = {}
30
+ value.each do |k, v|
31
+ if v.is_a?(String)
32
+ v = v.upcase
33
+
34
+ # The value had multiple sub-values
35
+ v = v.split(',') if v.index(',')
36
+ else
37
+ v = v.map { |val| val.is_a?(String) ? val.upcase : val }
38
+ end
39
+
40
+ new_val[k.upcase] = v
41
+ end
42
+
43
+ @value = new_val
44
+ elsif value.is_a?(String)
45
+ @value = self.class.string_to_array(value)
46
+ else
47
+ fail ArgumentError, 'You must either pass a string, or a key=>value array'
48
+ end
49
+ end
50
+
51
+ # Returns the current value.
52
+ #
53
+ # This method will always return a singular value. If this was a
54
+ # multi-value object, some decision will be made first on how to represent
55
+ # it as a string.
56
+ #
57
+ # To get the correct multi-value version, use getParts.
58
+ #
59
+ # @return string
60
+ def value
61
+ out = []
62
+ @value.each do |key, value|
63
+ out << "#{key}=#{value.is_a?(Array) ? value.join(',') : value}"
64
+ end
65
+ out.join(';').upcase
66
+ end
67
+
68
+ # Sets a multi-valued property.
69
+ #
70
+ # @param array parts
71
+ #
72
+ # @return void
73
+ def parts=(parts)
74
+ self.value = parts
75
+ end
76
+
77
+ # Returns a multi-valued property.
78
+ #
79
+ # This method always returns an array, if there was only a single value,
80
+ # it will still be wrapped in an array.
81
+ #
82
+ # @return array
83
+ def parts
84
+ @value
85
+ end
86
+
87
+ # Sets a raw value coming from a mimedir (iCalendar/vCard) file.
88
+ #
89
+ # This has been 'unfolded', so only 1 line will be passed. Unescaping is
90
+ # not yet done, but parameters are not included.
91
+ #
92
+ # @param string val
93
+ #
94
+ # @return void
95
+ def raw_mime_dir_value=(val)
96
+ self.value = val
97
+ end
98
+
99
+ # Returns a raw mime-dir representation of the value.
100
+ #
101
+ # @return string
102
+ def raw_mime_dir_value
103
+ value
104
+ end
105
+
106
+ # Returns the type of value.
107
+ #
108
+ # This corresponds to the VALUE= parameter. Every property also has a
109
+ # 'default' valueType.
110
+ #
111
+ # @return string
112
+ def value_type
113
+ 'RECUR'
114
+ end
115
+
116
+ # Returns the value, in the format it should be encoded for json.
117
+ #
118
+ # This method must always return an array.
119
+ #
120
+ # @return array
121
+ def json_value
122
+ values = {}
123
+ parts.each do |k, v|
124
+ values[k.downcase] = v
125
+ end
126
+ [values]
127
+ end
128
+
129
+ protected
130
+
131
+ # This method serializes only the value of a property. This is used to
132
+ # create xCard or xCal documents.
133
+ #
134
+ # @param Xml\Writer writer XML writer.
135
+ #
136
+ # @return void
137
+ def xml_serialize_value(writer)
138
+ value_type = self.value_type.downcase
139
+
140
+ json_value.each do |value|
141
+ writer.write_element(value_type, value)
142
+ end
143
+ end
144
+
145
+ public
146
+
147
+ # Parses an RRULE value string, and turns it into a struct-ish array.
148
+ #
149
+ # @param string value
150
+ #
151
+ # @return array
152
+ def self.string_to_array(value)
153
+ value = value.upcase
154
+ new_value = {}
155
+ value.split(';').each do |part|
156
+ # Skipping empty parts.
157
+ next if part.blank?
158
+
159
+ (part_name, part_value) = part.split('=')
160
+
161
+ # The value itself had multiple values..
162
+ part_value = part_value.split(',') if part_value.index(',')
163
+
164
+ new_value[part_name] = part_value
165
+ end
166
+
167
+ new_value
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,14 @@
1
+ module Tilia
2
+ module VObject
3
+ class Property
4
+ module ICalendar
5
+ require 'tilia/v_object/property/i_calendar/cal_address'
6
+ require 'tilia/v_object/property/i_calendar/date_time'
7
+ require 'tilia/v_object/property/i_calendar/date'
8
+ require 'tilia/v_object/property/i_calendar/duration'
9
+ require 'tilia/v_object/property/i_calendar/period'
10
+ require 'tilia/v_object/property/i_calendar/recur'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,60 @@
1
+ module Tilia
2
+ module VObject
3
+ class Property
4
+ # Integer property.
5
+ #
6
+ # This object represents INTEGER values. These are always a single integer.
7
+ # They may be preceeded by either + or -.
8
+ class IntegerValue < Property
9
+ # Sets a raw value coming from a mimedir (iCalendar/vCard) file.
10
+ #
11
+ # This has been 'unfolded', so only 1 line will be passed. Unescaping is
12
+ # not yet done, but parameters are not included.
13
+ #
14
+ # @param string val
15
+ #
16
+ # @return void
17
+ def raw_mime_dir_value=(val)
18
+ self.value = val.to_i
19
+ end
20
+
21
+ # Returns a raw mime-dir representation of the value.
22
+ #
23
+ # @return string
24
+ def raw_mime_dir_value
25
+ @value
26
+ end
27
+
28
+ # Returns the type of value.
29
+ #
30
+ # This corresponds to the VALUE= parameter. Every property also has a
31
+ # 'default' valueType.
32
+ #
33
+ # @return string
34
+ def value_type
35
+ 'INTEGER'
36
+ end
37
+
38
+ # Returns the value, in the format it should be encoded for json.
39
+ #
40
+ # This method must always return an array.
41
+ #
42
+ # @return array
43
+ def json_value
44
+ [value.to_i]
45
+ end
46
+
47
+ # Hydrate data from a XML subtree, as it would appear in a xCard or xCal
48
+ # object.
49
+ #
50
+ # @param array value
51
+ #
52
+ # @return void
53
+ def xml_value=(value)
54
+ value = value.map &:to_i
55
+ super(value)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,352 @@
1
+ module Tilia
2
+ module VObject
3
+ class Property
4
+ # Text property.
5
+ #
6
+ # This object represents TEXT values.
7
+ class Text < Property
8
+ # In case this is a multi-value property. This string will be used as a
9
+ # delimiter.
10
+ #
11
+ # @var string
12
+ attr_accessor :delimiter
13
+
14
+ # List of properties that are considered 'structured'.
15
+ #
16
+ # @var array
17
+ # RUBY: attr_accessor :structured_values
18
+
19
+ # Some text components have a minimum number of components.
20
+ #
21
+ # N must for instance be represented as 5 components, separated by ;, even
22
+ # if the last few components are unused.
23
+ #
24
+ # @var array
25
+ # RUBY: attr_accessor :minimum_property_values
26
+
27
+ # Creates the property.
28
+ #
29
+ # You can specify the parameters either in key=>value syntax, in which case
30
+ # parameters will automatically be created, or you can just pass a list of
31
+ # Parameter objects.
32
+ #
33
+ # @param Component root The root document
34
+ # @param string name
35
+ # @param string|array|null value
36
+ # @param array parameters List of parameters
37
+ # @param string group The vcard property group
38
+ #
39
+ # @return void
40
+ def initialize(root, name, value = nil, parameters = [], group = nil)
41
+ super(root, name, value, parameters, group)
42
+
43
+ @delimiter = ','
44
+ @structured_values = [
45
+ # vCard
46
+ 'N',
47
+ 'ADR',
48
+ 'ORG',
49
+ 'GENDER',
50
+ 'CLIENTPIDMAP',
51
+
52
+ # iCalendar
53
+ 'REQUEST-STATUS'
54
+ ]
55
+ @minimum_property_values = {
56
+ 'N' => 5,
57
+ 'ADR' => 7
58
+ }
59
+
60
+ # There's two types of multi-valued text properties:
61
+ # 1. multivalue properties.
62
+ # 2. structured value properties
63
+ #
64
+ # The former is always separated by a comma, the latter by semi-colon.
65
+ @delimiter = ';' if @structured_values.include?(name)
66
+ end
67
+
68
+ # Sets a raw value coming from a mimedir (iCalendar/vCard) file.
69
+ #
70
+ # This has been 'unfolded', so only 1 line will be passed. Unescaping is
71
+ # not yet done, but parameters are not included.
72
+ #
73
+ # @param string val
74
+ #
75
+ # @return void
76
+ def raw_mime_dir_value=(val)
77
+ self.value = Parser::MimeDir.unescape_value(val, @delimiter)
78
+ end
79
+
80
+ # Sets the value as a quoted-printable encoded string.
81
+ #
82
+ # @param string val
83
+ #
84
+ # @return void
85
+ def quoted_printable_value=(val)
86
+ val = Mail::Encodings::QuotedPrintable.decode(val)
87
+ val = val.gsub(/\n/, "\r\n").gsub(/\r\r/, "\r")
88
+
89
+ # force the correct encoding
90
+ begin
91
+ val.force_encoding(Encoding.find(@parameters['CHARSET'].to_s))
92
+ rescue
93
+ val.force_encoding(Encoding::UTF_8)
94
+ end
95
+
96
+ # Quoted printable only appears in vCard 2.1, and the only character
97
+ # that may be escaped there is ;. So we are simply splitting on just
98
+ # that.
99
+ #
100
+ # We also don't have to unescape \\, so all we need to look for is a
101
+ # that's not preceeded with a \.
102
+ matches = val.split(/ (?<!\\\\) ; /x)
103
+ self.value = matches
104
+ end
105
+
106
+ # Returns a raw mime-dir representation of the value.
107
+ #
108
+ # @return string
109
+ def raw_mime_dir_value
110
+ val = parts
111
+
112
+ if @minimum_property_values.key?(@name)
113
+ val << '' while val.size < @minimum_property_values[@name]
114
+ end
115
+
116
+ val = val.map do |item|
117
+ item = [item] unless item.is_a?(Array)
118
+
119
+ item = item.map do |sub_item|
120
+ sub_item.to_s.gsub(
121
+ /[\\;,\n\r]/,
122
+ '\\' => '\\\\',
123
+ ';' => '\;',
124
+ ',' => '\,',
125
+ "\n" => '\n',
126
+ "\r" => ''
127
+ )
128
+ end
129
+ item.join(',')
130
+ end
131
+
132
+ val.join(@delimiter)
133
+ end
134
+
135
+ # Returns the value, in the format it should be encoded for json.
136
+ #
137
+ # This method must always return an array.
138
+ #
139
+ # @return array
140
+ def json_value
141
+ # Structured text values should always be returned as a single
142
+ # array-item. Multi-value text should be returned as multiple items in
143
+ # the top-array.
144
+ return [parts] if @structured_values.include?(@name)
145
+
146
+ parts
147
+ end
148
+
149
+ # Returns the type of value.
150
+ #
151
+ # This corresponds to the VALUE= parameter. Every property also has a
152
+ # 'default' valueType.
153
+ #
154
+ # @return string
155
+ def value_type
156
+ 'TEXT'
157
+ end
158
+
159
+ # Turns the object back into a serialized blob.
160
+ #
161
+ # @return string
162
+ def serialize
163
+ # We need to kick in a special type of encoding, if it's a 2.1 vcard.
164
+ return super unless @root.document_type == Document::VCARD21
165
+
166
+ val = parts
167
+
168
+ if @minimum_property_values.key?(@name)
169
+ val << '' while val.size < @minimum_property_values[@name]
170
+ end
171
+
172
+ # Imploding multiple parts into a single value, and splitting the
173
+ # values with ;.
174
+ if val.size > 1
175
+ val = val.map do |v|
176
+ v.gsub(';', '\\;')
177
+ end
178
+ val = val.join(';')
179
+ else
180
+ val = val[0]
181
+ end
182
+
183
+ str = @name
184
+ str = "#{@group}.#{@name}" if @group
185
+
186
+ @parameters.each do |_key, param|
187
+ next if param.value == 'QUOTED-PRINTABLE'
188
+
189
+ str += ';' + param.serialize
190
+ end
191
+
192
+ # If the resulting value contains a \n, we must encode it as
193
+ # quoted-printable.
194
+ if val.index("\n")
195
+ str += ';ENCODING=QUOTED-PRINTABLE:'
196
+ last_line = str
197
+ out = ''
198
+
199
+ # The PHP built-in quoted-printable-encode does not correctly
200
+ # encode newlines for us. Specifically, the \r\n sequence must in
201
+ # vcards be encoded as =0D=OA and we must insert soft-newlines
202
+ # every 75 bytes.
203
+ val.bytes.each do |ord|
204
+ # These characters are encoded as themselves.
205
+ if ord >= 32 && ord <= 126
206
+ last_line += ord.chr
207
+ else
208
+ last_line += format('=%02X', ord)
209
+ end
210
+
211
+ next unless last_line.length >= 75
212
+ out += last_line + "=\r\n "
213
+ last_line = ''
214
+ end
215
+
216
+ out += last_line + "\r\n" unless last_line.blank?
217
+ return out
218
+ else
219
+ str += ':' + val
220
+ out = ''
221
+
222
+ while str.length > 0
223
+ if str.bytesize > 75
224
+ tmp = StringUtil.mb_strcut(str, 75)
225
+ out += tmp + "\r\n"
226
+ str = ' ' + str[tmp.length..-1]
227
+ else
228
+ out += str + "\r\n"
229
+ str = ''
230
+ break
231
+ end
232
+ end
233
+
234
+ return out
235
+ end
236
+ end
237
+
238
+ protected
239
+
240
+ # This method serializes only the value of a property. This is used to
241
+ # create xCard or xCal documents.
242
+ #
243
+ # @param Xml\Writer writer XML writer.
244
+ #
245
+ # @return void
246
+ def xml_serialize_value(writer)
247
+ values = parts
248
+
249
+ map = lambda do |items|
250
+ items.each_with_index do |item, i|
251
+ writer.write_element(
252
+ item,
253
+ values[i].blank? ? nil : values[i]
254
+ )
255
+ end
256
+ end
257
+
258
+ case @name
259
+ # Special-casing the REQUEST-STATUS property.
260
+ #
261
+ # See:
262
+ # http://tools.ietf.org/html/rfc6321#section-3.4.1.3
263
+ when 'REQUEST-STATUS'
264
+ writer.write_element('code', values[0])
265
+ writer.write_element('description', values[1])
266
+
267
+ writer.write_element('data', values[2]) if values[2]
268
+ when 'N'
269
+ map.call(
270
+ [
271
+ 'surname',
272
+ 'given',
273
+ 'additional',
274
+ 'prefix',
275
+ 'suffix'
276
+ ]
277
+ )
278
+ when 'GENDER'
279
+ map.call(
280
+ [
281
+ 'sex',
282
+ 'text'
283
+ ]
284
+ )
285
+ when 'ADR'
286
+ map.call(
287
+ [
288
+ 'pobox',
289
+ 'ext',
290
+ 'street',
291
+ 'locality',
292
+ 'region',
293
+ 'code',
294
+ 'country'
295
+ ]
296
+ )
297
+ when 'CLIENTPIDMAP'
298
+ map.call(
299
+ [
300
+ 'sourceid',
301
+ 'uri'
302
+ ]
303
+ )
304
+ else
305
+ super(writer)
306
+ end
307
+ end
308
+
309
+ public
310
+
311
+ # Validates the node for correctness.
312
+ #
313
+ # The following options are supported:
314
+ # - Node::REPAIR - If something is broken, and automatic repair may
315
+ # be attempted.
316
+ #
317
+ # An array is returned with warnings.
318
+ #
319
+ # Every item in the array has the following properties:
320
+ # * level - (number between 1 and 3 with severity information)
321
+ # * message - (human readable message)
322
+ # * node - (reference to the offending node)
323
+ #
324
+ # @param int options
325
+ #
326
+ # @return array
327
+ def validate(options = 0)
328
+ warnings = super(options)
329
+
330
+ if @minimum_property_values.key?(@name)
331
+ minimum = @minimum_property_values[@name]
332
+ parts = self.parts
333
+ if parts.size < minimum
334
+ warnings << {
335
+ 'level' => 1,
336
+ 'message' => "This property must have at least #{minimum} components. It only has #{parts.size}",
337
+ 'node' => self
338
+ }
339
+
340
+ if options & self.class::REPAIR > 0
341
+ parts << '' while parts.size < minimum
342
+ self.parts = parts
343
+ end
344
+ end
345
+ end
346
+
347
+ warnings
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end
@@ -0,0 +1,85 @@
1
+ module Tilia
2
+ module VObject
3
+ class Property
4
+ # Time property.
5
+ #
6
+ # This object encodes TIME values.
7
+ class Time < Text
8
+ # In case this is a multi-value property. This string will be used as a
9
+ # delimiter.
10
+ #
11
+ # @var string|null
12
+ attr_accessor :delimiter
13
+
14
+ # Returns the type of value.
15
+ #
16
+ # This corresponds to the VALUE= parameter. Every property also has a
17
+ # 'default' valueType.
18
+ #
19
+ # @return string
20
+ def value_type
21
+ 'TIME'
22
+ end
23
+
24
+ # Returns the value, in the format it should be encoded for json.
25
+ #
26
+ # This method must always return an array.
27
+ #
28
+ # @return array
29
+ def json_value
30
+ parts = DateTimeParser.parse_v_card_time(value)
31
+ time_str = ''
32
+
33
+ # Hour
34
+ if !parts['hour'].nil?
35
+ time_str += parts['hour']
36
+
37
+ time_str += ':' unless parts['minute'].nil?
38
+ else
39
+ # We know either minute or second _must_ be set, so we insert a
40
+ # dash for an empty value.
41
+ time_str += '-'
42
+ end
43
+
44
+ # Minute
45
+ if !parts['minute'].nil?
46
+ time_str += parts['minute']
47
+
48
+ time_str += ':' unless parts['second'].nil?
49
+ else
50
+ if parts['second']
51
+ # Dash for empty minute
52
+ time_str += '-'
53
+ end
54
+ end
55
+
56
+ # Second
57
+ time_str += parts['second'] unless parts['second'].nil?
58
+
59
+ # Timezone
60
+ time_str += parts['timezone'] unless parts['timezone'].nil?
61
+
62
+ [time_str]
63
+ end
64
+
65
+ # Hydrate data from a XML subtree, as it would appear in a xCard or xCal
66
+ # object.
67
+ #
68
+ # @param array value
69
+ #
70
+ # @return void
71
+ def xml_value=(value)
72
+ value = value.map do |v|
73
+ v.delete(':')
74
+ end
75
+ super(value)
76
+ end
77
+
78
+ def initialize(*args)
79
+ super(*args)
80
+ @delimiter = nil
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,30 @@
1
+ module Tilia
2
+ module VObject
3
+ class Property
4
+ # Unknown property.
5
+ #
6
+ # This object represents any properties not recognized by the parser.
7
+ # This type of value has been introduced by the jCal, jCard specs.
8
+ class Unknown < Text
9
+ # Returns the value, in the format it should be encoded for json.
10
+ #
11
+ # This method must always return an array.
12
+ #
13
+ # @return array
14
+ def json_value
15
+ [raw_mime_dir_value]
16
+ end
17
+
18
+ # Returns the type of value.
19
+ #
20
+ # This corresponds to the VALUE= parameter. Every property also has a
21
+ # 'default' valueType.
22
+ #
23
+ # @return string
24
+ def value_type
25
+ 'UNKNOWN'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end