mumboe-vpim 0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/CHANGES +510 -0
  2. data/COPYING +58 -0
  3. data/README +185 -0
  4. data/lib/vpim/address.rb +219 -0
  5. data/lib/vpim/agent/atomize.rb +104 -0
  6. data/lib/vpim/agent/base.rb +73 -0
  7. data/lib/vpim/agent/calendars.rb +173 -0
  8. data/lib/vpim/agent/handler.rb +26 -0
  9. data/lib/vpim/agent/ics.rb +161 -0
  10. data/lib/vpim/attachment.rb +102 -0
  11. data/lib/vpim/date.rb +222 -0
  12. data/lib/vpim/dirinfo.rb +277 -0
  13. data/lib/vpim/duration.rb +119 -0
  14. data/lib/vpim/enumerator.rb +32 -0
  15. data/lib/vpim/field.rb +614 -0
  16. data/lib/vpim/icalendar.rb +384 -0
  17. data/lib/vpim/maker/vcard.rb +16 -0
  18. data/lib/vpim/property/base.rb +193 -0
  19. data/lib/vpim/property/common.rb +315 -0
  20. data/lib/vpim/property/location.rb +38 -0
  21. data/lib/vpim/property/priority.rb +43 -0
  22. data/lib/vpim/property/recurrence.rb +69 -0
  23. data/lib/vpim/property/resources.rb +24 -0
  24. data/lib/vpim/repo.rb +261 -0
  25. data/lib/vpim/rfc2425.rb +367 -0
  26. data/lib/vpim/rrule.rb +591 -0
  27. data/lib/vpim/time.rb +40 -0
  28. data/lib/vpim/vcard.rb +1456 -0
  29. data/lib/vpim/version.rb +18 -0
  30. data/lib/vpim/vevent.rb +187 -0
  31. data/lib/vpim/view.rb +90 -0
  32. data/lib/vpim/vjournal.rb +58 -0
  33. data/lib/vpim/vpim.rb +65 -0
  34. data/lib/vpim/vtodo.rb +103 -0
  35. data/lib/vpim.rb +13 -0
  36. data/samples/README.mutt +93 -0
  37. data/samples/ab-query.rb +57 -0
  38. data/samples/agent.ru +10 -0
  39. data/samples/cmd-itip.rb +156 -0
  40. data/samples/ex_cpvcard.rb +55 -0
  41. data/samples/ex_get_vcard_photo.rb +22 -0
  42. data/samples/ex_mkv21vcard.rb +34 -0
  43. data/samples/ex_mkvcard.rb +64 -0
  44. data/samples/ex_mkyourown.rb +29 -0
  45. data/samples/ics-dump.rb +210 -0
  46. data/samples/ics-to-rss.rb +84 -0
  47. data/samples/mutt-aliases-to-vcf.rb +45 -0
  48. data/samples/osx-wrappers.rb +86 -0
  49. data/samples/reminder.rb +209 -0
  50. data/samples/rrule.rb +71 -0
  51. data/samples/tabbed-file-to-vcf.rb +390 -0
  52. data/samples/vcf-dump.rb +86 -0
  53. data/samples/vcf-lines.rb +61 -0
  54. data/samples/vcf-to-ics.rb +22 -0
  55. data/samples/vcf-to-mutt.rb +121 -0
  56. data/test/test_agent_atomize.rb +84 -0
  57. data/test/test_agent_calendars.rb +128 -0
  58. data/test/test_agent_ics.rb +96 -0
  59. data/test/test_all.rb +17 -0
  60. data/test/test_date.rb +120 -0
  61. data/test/test_dur.rb +41 -0
  62. data/test/test_field.rb +156 -0
  63. data/test/test_ical.rb +437 -0
  64. data/test/test_misc.rb +13 -0
  65. data/test/test_repo.rb +129 -0
  66. data/test/test_rrule.rb +1030 -0
  67. data/test/test_vcard.rb +973 -0
  68. data/test/test_view.rb +79 -0
  69. metadata +140 -0
@@ -0,0 +1,390 @@
1
+ #
2
+ # Filename: tabbed_file_to_vcards.rb
3
+ # Author: Dane G. Avilla
4
+ # Description:
5
+ # This script takes a tab-delimited text file with address info and attempts
6
+ # to parse each
7
+ # line of the file into a VCard.
8
+ #
9
+ # Usage: ruby tabbed_file_to_vcards.rb ./contacts.txt > vcards.vcf
10
+ #
11
+ # This command will create vcards and save them into vcards.vcf.
12
+ #
13
+ # License: Same as Ruby.
14
+ #
15
+
16
+ require 'vpim/vcard'
17
+
18
+ #
19
+ # Opens a file and attempts to parse out vcards. It is meant to work on a
20
+ # tab-delimited file with column names in the first line, followed by
21
+ # any number of records on the following lines.
22
+ #
23
+ class VCardParser
24
+
25
+ #
26
+ # Pass in a filename as a string, and get an array of VCard objects back.
27
+ #
28
+ def vcards_from_txt_file(filename)
29
+ #puts "Parsing input file #{filename}"
30
+ vcards = []
31
+ first_line = true
32
+ VCardField.reset_custom_fields
33
+ IO.foreach(filename) { |line|
34
+ #puts "Parsing line: #{line}"
35
+ if first_line == true
36
+ @headers = headers_from_line(line)
37
+ first_line = false
38
+ else
39
+ vcards << vcard_from_line(line)
40
+ end
41
+ }
42
+ vcards
43
+ end
44
+
45
+ protected
46
+ def headers_from_line(a_line)
47
+ a_line.upcase.split("\t")
48
+ end
49
+
50
+ def fields_from_line(a_line)
51
+ field_arr = headers_from_line(a_line)
52
+ fields = {}
53
+ @headers.each_index { |index|
54
+ fields[@headers[index]]= field_arr[index]
55
+ }
56
+ fields
57
+ end
58
+
59
+ def vcard_from_line(a_line)
60
+ #puts "vcard_from_line"
61
+ # Parse the line from a tab-delimited text file.
62
+ # The tricky part is that there may
63
+ # be fields in the txt file which have commas between opening and closing
64
+ # quotes, so don't just split on ','.
65
+
66
+ # Get a hash of field names and values
67
+ fields = fields_from_line(a_line)
68
+ #puts "FirstName: " + fields["FIRST_NAME"]
69
+ # 1. Look for the pattern /\".*,.*\"/
70
+ # 2. If found, save that pattern, and then substitute it with a
71
+ # dynamic placeholder.
72
+
73
+ # 3. Split the line on commas.
74
+
75
+ # 4. For each item in the split, replace the substituted pattern
76
+ # with the source pattern.
77
+
78
+ #p fields
79
+
80
+ # At this point, we should have an array of string values matching
81
+ # the order of @headers. Construct a VCard using the header keys and
82
+ # the parsed values
83
+ vcard = Vpim::Vcard.create
84
+
85
+ # Add the name field
86
+ vcard << VCardField.create_n(fields["LAST_NAME"], fields["FIRST_NAME"],
87
+ fields["MIDDLE_NAME"], fields["TITLE"], fields["SUFFIX"])
88
+ # Add the formal name display field
89
+ vcard << VCardField.create_fn(fields["LAST_NAME"], fields["FIRST_NAME"],
90
+ fields["MIDDLE_NAME"], fields["TITLE"], fields["SUFFIX"])
91
+ # Add Company & Department info
92
+ vcard << VCardField.create_org(fields["COMPANY"], fields["DEPARTMENT"])
93
+ # Add Job Title info
94
+ vcard << VCardField.create_job_title(fields["JOB_TITLE"])
95
+ # Add Phone Numbers
96
+ vcard << VCardField.create_work_fax(fields["BUSINESS_FAX"])
97
+ vcard << VCardField.create_work_phone(fields["BUSINESS_PHONE"])
98
+ vcard << VCardField.create_work_phone(fields["BUSINESS_PHONE_2"])
99
+ vcard << VCardField.create_home_fax(fields["HOME_FAX"])
100
+ vcard << VCardField.create_home_phone(fields["HOME_PHONE"])
101
+ vcard << VCardField.create_home_phone(fields["HOME_PHONE_2"])
102
+ vcard << VCardField.create_cell_phone(fields["MOBILE_PHONE"])
103
+ vcard << VCardField.create_pager(fields["PAGER"])
104
+ vcard << VCardField.create_custom_phone(fields["OTHER_PHONE"], "other")
105
+ # Add Business Address
106
+ vcard << VCardField.create_business_address(
107
+ fields["BUSINESS_STREET"],
108
+ fields["BUSINESS_STREET_2"],
109
+ fields["BUSINESS_STREET_3"],
110
+ fields["BUSINESS_CITY"],
111
+ fields["BUSINESS_STATE"],
112
+ fields["BUSINESS_POSTAL_CODE"],
113
+ fields["BUSINESS_COUNTRY"]
114
+ )
115
+ # Add Home Address
116
+ vcard << VCardField.create_home_address(
117
+ fields["HOME_STREET"],
118
+ fields["HOME_STREET_2"],
119
+ fields["HOME_STREET_3"],
120
+ fields["HOME_CITY"],
121
+ fields["HOME_STATE"],
122
+ fields["HOME_POSTAL_CODE"],
123
+ fields["HOME_COUNTRY"]
124
+ )
125
+ # Add Other Address
126
+ vcard << VCardField.create_other_address(
127
+ "Sample Other Address",
128
+ fields["OTHER_STREET"],
129
+ fields["OTHER_STREET_2"],
130
+ fields["OTHER_STREET_3"],
131
+ fields["OTHER_CITY"],
132
+ fields["OTHER_STATE"],
133
+ fields["OTHER_POSTAL_CODE"],
134
+ fields["OTHER_COUNTRY"]
135
+ )
136
+
137
+ # Add Emails
138
+ vcard << VCardField.create_work_email(fields["E-MAIL_ADDRESS"])
139
+ vcard << VCardField.create_home_email(fields["E-MAIL_2_ADDRESS"])
140
+ vcard << VCardField.create_other_email(fields["E-MAIL_3_ADDRESS"], "other")
141
+
142
+ # Add a note
143
+ vcard << VCardField.create_note(fields["NOTES"])
144
+
145
+ vcard
146
+ end
147
+ end
148
+
149
+ #
150
+ # Subclass of Vpim::DirectoryInfo::Field adds a number of helpful methods for
151
+ # creating VCard fields.
152
+ #
153
+ class VCardField < Vpim::DirectoryInfo::Field
154
+ def VCardField.reset_custom_fields
155
+ @@custom_number = 1
156
+ end
157
+
158
+ #
159
+ # Create a name field: "N"
160
+ #
161
+ def VCardField.create_n (last, first=nil, middle=nil, prefix=nil, suffix=nil)
162
+ VCardField.create('N', "#{last};#{first};#{middle};#{prefix};#{suffix}")
163
+ end
164
+
165
+ protected
166
+ def VCardField.valid_string(a_str)
167
+ return a_str != nil && a_str.length > 0
168
+ end
169
+
170
+ public
171
+
172
+ #
173
+ # Create a formal name field: "FN"
174
+ #
175
+ def VCardField.create_fn (last, first=nil, middle=nil, prefix=nil, suffix=nil)
176
+ name = ""
177
+ if valid_string(prefix) then name << "#{prefix} " end
178
+ if valid_string(first) then name << "#{first} " end
179
+ if valid_string(middle) then name << "#{middle} " end
180
+ if valid_string(last) then name << "#{last} " end
181
+ if valid_string(suffix) then name << "#{suffix} " end
182
+ VCardField.create('FN', "#{name}")
183
+ end
184
+
185
+ #
186
+ # Create a formal name field: "ORG"
187
+ #
188
+ def VCardField.create_org (organization_name, department_name=nil)
189
+ VCardField.create("ORG", "#{organization_name};#{department_name}")
190
+ end
191
+
192
+ #
193
+ # Create a title field: "TITLE"
194
+ #
195
+ def VCardField.create_job_title(title)
196
+ VCardField.create("TITLE", title)
197
+ end
198
+
199
+ #
200
+ # Create an email field: "EMAIL" with type="INTERNET"
201
+ #
202
+ # For _type_, use Ruby symbols :WORK or :HOME.
203
+ #
204
+ def VCardField.create_internet_email(address, type=:WORK, preferred_email=false)
205
+ if preferred_email == true
206
+ VCardField.create("EMAIL", address,
207
+ "type" => ["INTERNET", type.to_s, "pref"])
208
+ else
209
+ VCardField.create("EMAIL", address,
210
+ "type" => ["INTERNET", type.to_s])
211
+ end
212
+ end
213
+
214
+ protected
215
+ def VCardField.next_custom_name
216
+ name = "item#{@@custom_number}"
217
+ @@custom_number = @@custom_number + 1
218
+ name
219
+ end
220
+
221
+ def VCardField.create_phone(phone_num, is_preferred = false, type_arr = ["WORK"],
222
+ custom_name = nil)
223
+
224
+ field_name = ""
225
+ if custom_name != nil
226
+ field_name << next_custom_name
227
+ field_name << "."
228
+ end
229
+
230
+ # Flatten the array so we can add additional items to it.
231
+ type_arr = [type_arr].flatten
232
+ # If this phone number is preferred, then add that into the type array.
233
+ if is_preferred
234
+ type_arr << "pref"
235
+ end
236
+ # Create the TEL field.
237
+ ret_val = [VCardField.create("#{field_name}TEL", phone_num, "type" => type_arr)]
238
+ # If we need a custom field . . .
239
+ if custom_name != nil
240
+ ret_val << VCardField.create("#{field_name}X-ABLabel", custom_name)
241
+ end
242
+ ret_val
243
+ end
244
+
245
+ public
246
+ def VCardField.create_note(note_text)
247
+ VCardField.create("NOTE", note_text)
248
+ end
249
+
250
+ def VCardField.create_custom_phone(phone_number, custom_name, is_preferred = false)
251
+ VCardField.create_phone(phone_number, is_preferred, ["HOME"], custom_name)
252
+ end
253
+
254
+ def VCardField.create_pager(pager_number, is_preferred = false)
255
+ VCardField.create_phone(pager_number, is_preferred, ["PAGER"])
256
+ end
257
+
258
+ def VCardField.create_work_fax(fax_number, is_preferred = false)
259
+ VCardField.create_phone(fax_number, is_preferred, ["FAX", "WORK"])
260
+ end
261
+
262
+ def VCardField.create_work_phone(phone_number, is_preferred = false)
263
+ VCardField.create_phone(phone_number, is_preferred, ["WORK"])
264
+ end
265
+
266
+ def VCardField.create_home_fax(fax_number, is_preferred = false)
267
+ VCardField.create_phone(fax_number, is_preferred, ["FAX", "HOME"])
268
+ end
269
+
270
+ def VCardField.create_home_phone(phone_number, is_preferred = false)
271
+ VCardField.create_phone(phone_number, is_preferred, ["HOME"])
272
+ end
273
+
274
+ def VCardField.create_cell_phone(phone_number, is_preferred = false)
275
+ VCardField.create_phone(phone_number, is_preferred, ["CELL"])
276
+ end
277
+
278
+ def VCardField.create_other_address(
279
+ address_label,
280
+ street,
281
+ street2 = "",
282
+ street3 = "",
283
+ city = "",
284
+ state = "",
285
+ postal_code = "",
286
+ country = "",
287
+ is_preferred = false
288
+ )
289
+ VCardField.create_address(street, street2, street3, city, state,
290
+ postal_code, country, is_preferred, ["HOME"], address_label)
291
+ end
292
+
293
+ def VCardField.create_home_address(
294
+ street,
295
+ street2 = "",
296
+ street3 = "",
297
+ city = "",
298
+ state = "",
299
+ postal_code = "",
300
+ country = "",
301
+ is_preferred = false
302
+ )
303
+ VCardField.create_address(street, street2, street3, city, state,
304
+ postal_code, country, is_preferred, ["HOME"])
305
+ end
306
+
307
+ def VCardField.create_business_address(
308
+ street,
309
+ street2 = "",
310
+ street3 = "",
311
+ city = "",
312
+ state = "",
313
+ postal_code = "",
314
+ country = "",
315
+ is_preferred = false
316
+ )
317
+ VCardField.create_address(street, street2, street3, city, state,
318
+ postal_code, country, is_preferred, ["WORK"])
319
+ end
320
+
321
+ def VCardField.create_work_email(address, is_preferred = false)
322
+ VCardField.create_email(address, is_preferred, ["WORK"])
323
+ end
324
+
325
+ def VCardField.create_home_email(address, is_preferred = false)
326
+ VCardField.create_email(address, is_preferred, ["HOME"])
327
+ end
328
+
329
+ def VCardField.create_other_email(address, custom_name, is_preferred = false)
330
+ VCardField.create_email(address, is_preferred, ["WORK"], custom_name)
331
+ end
332
+
333
+ protected
334
+ def VCardField.create_email(address, is_preferred, type_arr = ["WORK"], custom_name = nil)
335
+ name = ""
336
+ if custom_name != nil
337
+ name << next_custom_name
338
+ name << "."
339
+ end
340
+ if is_preferred
341
+ type_arr << "pref"
342
+ end
343
+ ret_val = [VCardField.create("#{name}EMAIL", address, "type" => type_arr)]
344
+ if custom_name != nil
345
+ ret_val << VCardField.create("#{name}X-ABLabel", custom_name)
346
+ end
347
+ ret_val
348
+ end
349
+
350
+ def VCardField.create_address(
351
+ street,
352
+ street2 = "",
353
+ street3 = "",
354
+ city = "",
355
+ state = "",
356
+ postal_code = "",
357
+ country = "",
358
+ is_preferred = false,
359
+ type_arr = ["WORK"],
360
+ other_label = nil)
361
+ # Addresses need custom names, so get the next custom name for this
362
+ # VCard
363
+ name = next_custom_name
364
+ # Construct the address string by making an array of the fields, and
365
+ # then joining them with ';' as the separator.
366
+ address_str = [street, street2, street3, city, state, postal_code, country]
367
+ # If this is preferred, add that type.
368
+ if is_preferred
369
+ type_arr << "pref"
370
+ end
371
+ # Return an array with two lines, one defining the address, the second
372
+ # defining something else . . . is this the locale? Not sure, but this
373
+ # is how Mac OS X 10.3.6 exports address fields -> VCards.
374
+ fields = [
375
+ VCardField.create("#{name}.ADR", address_str.join(';'), "type" => type_arr),
376
+ VCardField.create("#{name}.X-ABADR", "us"),
377
+ ]
378
+ if other_label != nil
379
+ fields << VCardField.create("#{name}.X-ABLabel", "#{other_label}")
380
+ end
381
+ fields
382
+ end
383
+ end
384
+
385
+ parser = VCardParser.new
386
+ cards = parser.vcards_from_txt_file(ARGV[0])
387
+ #puts ""
388
+ cards.each { |card|
389
+ puts card.to_s
390
+ }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $-w = true
4
+ $:.unshift File.dirname($0) + '/../lib'
5
+
6
+ require 'pp'
7
+ require 'getoptlong'
8
+ require 'vpim/vcard'
9
+
10
+ HELP =<<EOF
11
+ Usage: #{$0} <vcard>...
12
+
13
+ Options
14
+ -h,--help Print this helpful message.
15
+ -n,--name Print the vCard name.
16
+ -d,--debug Print debug information.
17
+
18
+ Examples:
19
+ EOF
20
+
21
+ opt_name = nil
22
+ opt_debug = nil
23
+
24
+ opts = GetoptLong.new(
25
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
26
+ [ "--name", "-n", GetoptLong::NO_ARGUMENT ],
27
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ]
28
+ )
29
+
30
+ opts.each do |opt, arg|
31
+ case opt
32
+ when "--help" then
33
+ puts HELP
34
+ exit 0
35
+
36
+ when "--name" then
37
+ opt_name = true
38
+
39
+ when "--debug" then
40
+ opt_debug = true
41
+ end
42
+ end
43
+
44
+ if ARGV.length < 1
45
+ puts "no vcard files specified, try -h!"
46
+ exit 1
47
+ end
48
+
49
+ ARGV.each do |file|
50
+
51
+ cards = Vpim::Vcard.decode(open(file))
52
+
53
+ cards.each do |card|
54
+ card.each do |field|
55
+ puts "..#{field.name.capitalize}=#{field.value.inspect}"
56
+
57
+ if field.group
58
+ puts " group=#{field.group}"
59
+ end
60
+
61
+ field.each_param do |param, values|
62
+ puts " #{param}=[#{values.join(", ")}]"
63
+ end
64
+ end
65
+
66
+ if opt_name
67
+ begin
68
+ puts "#name=#{card.name.formatted}"
69
+ rescue
70
+ puts "! failed to decode name!"
71
+ end
72
+ end
73
+
74
+
75
+ if opt_debug
76
+ card.groups.sort.each do |group|
77
+ card.enum_by_group(group).each do |field|
78
+ puts "#{group} -> #{field.inspect}"
79
+ end
80
+ end
81
+ end
82
+
83
+ puts ""
84
+ end
85
+ end
86
+
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $-w = true
4
+ $:.unshift File.dirname($0) + '/../lib'
5
+
6
+ require 'pp'
7
+ require 'getoptlong'
8
+ require 'vpim/vcard'
9
+
10
+ HELP =<<EOF
11
+ Usage: #{$0} <vcard>...
12
+
13
+ Options
14
+ -h,--help Print this helpful message.
15
+
16
+ Examples:
17
+ EOF
18
+
19
+ opt_name = nil
20
+ opt_debug = nil
21
+
22
+ opts = GetoptLong.new(
23
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
24
+ [ "--name", "-n", GetoptLong::NO_ARGUMENT ],
25
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ]
26
+ )
27
+
28
+ opts.each do |opt, arg|
29
+ case opt
30
+ when "--help" then
31
+ puts HELP
32
+ exit 0
33
+
34
+ when "--name" then
35
+ opt_name = true
36
+
37
+ when "--debug" then
38
+ opt_debug = true
39
+ end
40
+ end
41
+
42
+ if ARGV.length < 1
43
+ puts "no vcard files specified, try -h!"
44
+ exit 1
45
+ end
46
+
47
+ ARGV.each do |file|
48
+
49
+ cards = Vpim::Vcard.decode(open(file))
50
+
51
+ cards.each do |card|
52
+ card.lines.each_with_index do |line, i|
53
+ print line.name
54
+ if line.group.length > 0
55
+ print " (", line.group, ")"
56
+ end
57
+ print ": ", line.value.inspect, "\n"
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vpim/vcard'
4
+ require 'vpim/icalendar'
5
+
6
+ $in = ARGV.first ? File.open(ARGV.shift) : $stdin
7
+ $out = ARGV.first ? File.open(ARGV.shift, 'w') : $stdout
8
+
9
+ cal = Vpim::Icalendar.create
10
+
11
+ Vpim::Vcard.decode($in).each do |card|
12
+ if card.birthday
13
+ cal.push Vpim::Icalendar::Vevent.create_yearly(
14
+ card.birthday,
15
+ "Birthday for #{card['fn'].strip}"
16
+ )
17
+ $stderr.puts "#{card['fn']} -> bday #{cal.events.last.dtstart}"
18
+ end
19
+ end
20
+
21
+ puts cal.encode
22
+
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # For a query command, mutt expects output of the form:
4
+ #
5
+ # informational line
6
+ # <email>TAB<name>[TAB<other info>]
7
+ # ...
8
+ #
9
+ # For an alias command, mutt expects output of the form:
10
+ # alias NICKNAME EMAIL
11
+ #
12
+ # NICKNAME shouldn't have spaces, and EMAIL can be either "user@example.com",
13
+ # "<user@example.com>", or "User <user@example.com>".
14
+
15
+ $-w = true
16
+ $:.unshift File.dirname($0) + '/../lib'
17
+
18
+
19
+ require 'getoptlong'
20
+ require 'vpim/vcard'
21
+
22
+ HELP =<<EOF
23
+ Usage: vcf-to-mutt.rb [--aliases] [query]
24
+
25
+ Queries a vCard file and prints the results in Mutt's query result format, or
26
+ as a Mutt alias file. No query matches all vCards.
27
+
28
+ The query is matched against all fields, so you can use 'climbers' to match all
29
+ vCards that has that string in the Notes field if you want to email all the
30
+ rock climbers you know. Or you can query all vCards with addresses in a
31
+ particular city, you get the idea.
32
+
33
+ Options
34
+ -h,--help Print this helpful message.
35
+ -a,--aliases Output an alias file, otherwise output a query response.
36
+
37
+ Examples:
38
+
39
+ Put in your muttrc file (either ~/.muttrc or ~/.mutt/muttrc) a line such as:
40
+
41
+ set query_command = "vcf-to-mutt.rb '%s' < ~/mycards.vcf"
42
+
43
+ Bugs:
44
+
45
+ The aliases output file bases the alias name on the nickname, or the full
46
+ name, but either way, they aren't guaranteed to be unique if you have more than
47
+ email address in a vCard, or more than one vCard for the same nickname or full
48
+ name.
49
+ EOF
50
+
51
+ opt_query = ''
52
+ opt_aliases = false
53
+
54
+ opts = GetoptLong.new(
55
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
56
+ [ "--aliases", "-a", GetoptLong::NO_ARGUMENT ]
57
+ )
58
+
59
+ opts.each do |opt, arg|
60
+ case opt
61
+ when "--help" then
62
+ puts HELP
63
+ exit 0
64
+
65
+ when "--aliases" then
66
+ opt_aliases = true
67
+ end
68
+ end
69
+
70
+ opt_query = ARGV.first
71
+
72
+ module Mutt
73
+ def Mutt.vcard_query(cards, query)
74
+ query = query.downcase if query
75
+ cards.find_all do |card|
76
+ card.detect do |f|
77
+ !query || f.value.downcase.include?(query)
78
+ end
79
+ end
80
+ end
81
+
82
+ def Mutt.query_print(cards, caption)
83
+ puts caption
84
+
85
+ cards.each do
86
+ |vcard|
87
+ # find the email addresses
88
+ vcard.enum_by_name("email").each do |f|
89
+ nn = vcard.nickname
90
+ nn = nn ? "\t#{nn}" : ""
91
+ puts "#{f.value}\t#{vcard['fn']}#{nn}"
92
+ end
93
+ end
94
+ end
95
+
96
+ def Mutt.alias_print(cards)
97
+ cards.each do
98
+ |vcard|
99
+ # find the email addresses
100
+ vcard.enum_by_name("email").each do |f|
101
+ em = f.value
102
+ fn = vcard['fn']
103
+ nn = vcard.nickname || fn.gsub(/\s+/,'')
104
+ puts "alias #{nn} #{fn} <#{em}>"
105
+ end
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ cards = Vpim::Vcard.decode($stdin)
112
+
113
+ matches = Mutt::vcard_query(cards, opt_query)
114
+
115
+ if opt_aliases
116
+ Mutt::alias_print(matches)
117
+ else
118
+ qstr = opt_query == '' ? '<all records>' : opt_query;
119
+ Mutt::query_print(matches, "Query #{qstr} against #{cards.size} vCards:")
120
+ end
121
+