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.
- data/CHANGES +510 -0
- data/COPYING +58 -0
- data/README +185 -0
- data/lib/vpim/address.rb +219 -0
- data/lib/vpim/agent/atomize.rb +104 -0
- data/lib/vpim/agent/base.rb +73 -0
- data/lib/vpim/agent/calendars.rb +173 -0
- data/lib/vpim/agent/handler.rb +26 -0
- data/lib/vpim/agent/ics.rb +161 -0
- data/lib/vpim/attachment.rb +102 -0
- data/lib/vpim/date.rb +222 -0
- data/lib/vpim/dirinfo.rb +277 -0
- data/lib/vpim/duration.rb +119 -0
- data/lib/vpim/enumerator.rb +32 -0
- data/lib/vpim/field.rb +614 -0
- data/lib/vpim/icalendar.rb +384 -0
- data/lib/vpim/maker/vcard.rb +16 -0
- data/lib/vpim/property/base.rb +193 -0
- data/lib/vpim/property/common.rb +315 -0
- data/lib/vpim/property/location.rb +38 -0
- data/lib/vpim/property/priority.rb +43 -0
- data/lib/vpim/property/recurrence.rb +69 -0
- data/lib/vpim/property/resources.rb +24 -0
- data/lib/vpim/repo.rb +261 -0
- data/lib/vpim/rfc2425.rb +367 -0
- data/lib/vpim/rrule.rb +591 -0
- data/lib/vpim/time.rb +40 -0
- data/lib/vpim/vcard.rb +1456 -0
- data/lib/vpim/version.rb +18 -0
- data/lib/vpim/vevent.rb +187 -0
- data/lib/vpim/view.rb +90 -0
- data/lib/vpim/vjournal.rb +58 -0
- data/lib/vpim/vpim.rb +65 -0
- data/lib/vpim/vtodo.rb +103 -0
- data/lib/vpim.rb +13 -0
- data/samples/README.mutt +93 -0
- data/samples/ab-query.rb +57 -0
- data/samples/agent.ru +10 -0
- data/samples/cmd-itip.rb +156 -0
- data/samples/ex_cpvcard.rb +55 -0
- data/samples/ex_get_vcard_photo.rb +22 -0
- data/samples/ex_mkv21vcard.rb +34 -0
- data/samples/ex_mkvcard.rb +64 -0
- data/samples/ex_mkyourown.rb +29 -0
- data/samples/ics-dump.rb +210 -0
- data/samples/ics-to-rss.rb +84 -0
- data/samples/mutt-aliases-to-vcf.rb +45 -0
- data/samples/osx-wrappers.rb +86 -0
- data/samples/reminder.rb +209 -0
- data/samples/rrule.rb +71 -0
- data/samples/tabbed-file-to-vcf.rb +390 -0
- data/samples/vcf-dump.rb +86 -0
- data/samples/vcf-lines.rb +61 -0
- data/samples/vcf-to-ics.rb +22 -0
- data/samples/vcf-to-mutt.rb +121 -0
- data/test/test_agent_atomize.rb +84 -0
- data/test/test_agent_calendars.rb +128 -0
- data/test/test_agent_ics.rb +96 -0
- data/test/test_all.rb +17 -0
- data/test/test_date.rb +120 -0
- data/test/test_dur.rb +41 -0
- data/test/test_field.rb +156 -0
- data/test/test_ical.rb +437 -0
- data/test/test_misc.rb +13 -0
- data/test/test_repo.rb +129 -0
- data/test/test_rrule.rb +1030 -0
- data/test/test_vcard.rb +973 -0
- data/test/test_view.rb +79 -0
- 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
|
+
}
|
data/samples/vcf-dump.rb
ADDED
@@ -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
|
+
|