vpim 0.597 → 0.602

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
data/test/test_all.rb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $-w = true
4
+ $:.unshift File.dirname($0)
5
+
6
+ require 'test/unit'
7
+
8
+ require 'test_date.rb'
9
+ require 'test_dur.rb'
10
+ require 'test_field.rb'
11
+ require 'test_ical.rb'
12
+ require 'test_rrule.rb'
13
+ require 'test_vcard.rb'
14
+