File Coverage

lib/Text/vCard/Node.pm
Criterion Covered Total %
statement 254 260 97.6
branch 97 102 95.1
condition 45 60 75.0
subroutine 34 36 94.4
pod 11 11 100.0
total 441 469 94.0


line stmt bran cond sub pod time code
1             package Text::vCard::Node;
2             $Text::vCard::Node::VERSION = '3.09';
3 16     16   53818 use strict;
  16         30  
  16         346  
4 16     16   46 use warnings;
  16         14  
  16         275  
5 16     16   44 use Carp;
  16         18  
  16         656  
6 16     16   6923 use Encode;
  16         98126  
  16         1036  
7 16     16   5614 use MIME::Base64 3.07;
  16         6873  
  16         788  
8 16     16   5729 use MIME::QuotedPrint 3.07;
  16         2767  
  16         703  
9 16     16   6028 use Unicode::LineBreak;
  16         164331  
  16         679  
10 16     16   6874 use Text::Wrap;
  16         31804  
  16         762  
11 16     16   76 use vars qw ( $AUTOLOAD );
  16         20  
  16         34651  
12              
13             =head1 NAME
14              
15             Text::vCard::Node - Object for each node (line) of a vCard
16              
17             =head1 SYNOPSIS
18              
19             use Text::vCard::Node;
20              
21             my %data = (
22             'param' => {
23             'HOME,PREF' => 'undef',
24             },
25             'value' => ';;First work address - street;Work city;London;Work PostCode;CountryName',
26             );
27              
28             my $node = Text::vCard::Node->new({
29             node_type => 'address', # Auto upper cased
30             fields => ['po_box','extended','street','city','region','post_code','country'],
31             data => \%data,
32             });
33              
34             =head1 DESCRIPTION
35              
36             Package used by Text::vCard so that each element: ADR, N, TEL etc are objects.
37              
38             You should not need to use this module directly, L does it all for you.
39              
40             =head1 METHODS
41              
42             =head2 new()
43              
44             my $node = Text::vCard::Node->new({
45             node_type => 'address', # Auto upper cased
46             fields => \['po_box','extended','street','city','region','post_code','country'],
47             data => \%data,
48             });
49              
50             =head2 value()
51              
52             # Get the value for a standard single value node
53             my $value = $node->value();
54              
55             # Or set the value
56             $node->value('New value');
57            
58             =head2 other()'s
59              
60             # The fields supplied in the conf area also methods.
61             my $po_box = $node->po_box(); # if the node was an ADR.
62            
63             # Set the value.
64             my $street = $node->street('73 Sesame Street');
65              
66             =cut
67              
68             sub new {
69 322     322 1 2741 my ( $proto, $conf ) = @_;
70 322   100     767 my $class = ref($proto) || $proto;
71 322         270 my $self = {};
72 322 100       652 carp "No fields defined" unless defined $conf->{'fields'};
73             carp "fields is not an array ref"
74 321 100       651 unless ref( $conf->{'fields'} ) eq 'ARRAY';
75              
76 320         323 bless( $self, $class );
77              
78 319   100     620 $self->{encoding_out} = $conf->{encoding_out} || 'UTF-8';
79              
80             $self->{node_type} = uc( $conf->{node_type} )
81 319 100       650 if defined $conf->{node_type};
82 319 100       637 $self->group( $conf->{group} ) if defined $conf->{group};
83              
84             # Store the field order.
85 319         269 $self->{'field_order'} = $conf->{'fields'};
86              
87             # store the actual field names so we can look them up
88 319         229 my %fields;
89 319         214 map { $fields{$_} = 1 } @{ $self->{'field_order'} };
  757         910  
  319         388  
90 319         330 $self->{'field_lookup'} = \%fields;
91              
92 319 100       536 if ( defined $conf->{'data'} ) {
93              
94             # Populate now, rather than later (via AUTOLOAD)
95             # store values into object
96 316 100       449 if ( defined $conf->{'data'}->{'params'} ) {
97 139         100 my %params;
98              
99             # Loop through array
100 139         169 foreach my $param_hash ( @{ $conf->{'data'}->{'params'} } ) {
  139         187  
101 192         150 while ( my ( $key, $value ) = each %{$param_hash} ) {
  384         1006  
102 192         191 my $t = 'type';
103              
104             # go through each key/value pair
105 192         198 my $param_list = $key;
106 192 100       246 if ( defined $value ) {
107 175         109 $t = $key;
108              
109             # use value, not key as its 'type' => 'CELL',
110             # not 'CELL' => undef
111 175         139 $param_list = $value;
112             }
113              
114             # These values might as well be useful for
115             # something. Also get rid of any whitespace
116             # pollution.
117 192         423 for my $p ( split /\s*,\s*/, $param_list ) {
118 202         973 $p =~ s/^\s*(.*?)\s*$/\L$1/;
119 202         276 $p =~ s/\s+/ /g;
120 202         526 $params{$p} = lc $t;
121             }
122             }
123             }
124 139         183 $self->{params} = \%params;
125             }
126              
127 316 100       775 if ( defined $conf->{'data'}->{'value'} ) {
128              
129             # Store the actual data into the object
130              
131 280 100 66     426 if ( $self->is_type('q') or $self->is_type('quoted-printable') ) {
132              
133 9         11 my $value = $conf->{data}{value};
134 9         91 my $mime_decoded = MIME::QuotedPrint::decode($value);
135 9         29 my $encode_decoded = Encode::decode( 'UTF-8', $mime_decoded );
136 9         319 my $unescaped = $self->_unescape($encode_decoded);
137 9         17 $conf->{'data'}->{'value'} = $unescaped;
138             }
139              
140 280 100 66     328 if ( $self->is_type('b') or $self->is_type('base64') ) {
141              
142             # Don't Encode::decode() $mime_decoded because it is usually
143             # (99% of the time) a binary value like a photo and not a
144             # string.
145             #
146             # Also do not escape binary values.
147              
148 1         1 my $value = $conf->{data}{value};
149 1         15 my $mime_decoded = MIME::Base64::decode($value);
150 1         2 $conf->{data}{value} = $mime_decoded;
151              
152             # mimic what goes on below
153 1         2 @{$self}{ @{ $self->{field_order} } }
  1         1  
154 1         2 = ( $conf->{data}{value} );
155             } else {
156              
157             # the -1 on split is so ;; values create elements in
158             # the array
159 279         943 my @elements = split /(?{data}{value}, -1;
160 279 100 100     844 if ( defined $self->{node_type}
    100          
161             && $self->{node_type} eq 'ORG' )
162             {
163 4         10 my @unescaped = $self->_unescape_list(@elements);
164              
165 4         7 $self->{'name'} = shift(@unescaped);
166 4 100       15 $self->{'unit'} = \@unescaped if scalar(@unescaped) > 0;
167             }
168              
169             # no need for explicit scalar
170 275         412 elsif ( @elements <= @{ $self->{field_order} } ) {
171 274         390 my @unescaped = $self->_unescape_list(@elements);
172              
173             # set the field values as the data
174             # e.g. $self->{street} = 'The street'
175 274         238 @{$self}{ @{ $self->{field_order} } } = @unescaped;
  274         660  
  274         258  
176             } else {
177             carp sprintf(
178             'Data value had %d elements expecting %d or less.',
179             scalar @elements,
180 1         3 scalar @{ $self->{field_order} }
  1         93  
181             );
182             }
183             }
184             }
185             }
186 318         576 return $self;
187             }
188              
189             sub _unescape {
190 693     693   631 my ( $self, $value ) = @_;
191 693         666 $value =~ s|\\([\\,;])|$1|g;
192 693         972 return $value;
193             }
194              
195             sub _unescape_list {
196 278     278   377 my ( $self, @values ) = @_;
197 278         264 return map { $self->_unescape($_) } @values;
  684         677  
198             }
199              
200             =head2 node_type
201              
202             Returns the type of the node itself, e.g. ADR.
203              
204             =cut
205              
206             sub node_type {
207 175     175 1 246 $_[0]->{node_type};
208             }
209              
210             =head2 unit()
211              
212             my @units = @{ $org_node->unit() };
213             $org_node->unit( [ 'Division', 'Department', 'Sub-department' ] );
214              
215             As ORG allows unlimited numbers of 'units' as well as and organisation
216             'name', this method is a specific case for accessing those values, they
217             are always returned as an array reference, and should always be set
218             as an array reference.
219              
220             =cut
221              
222             sub unit {
223 4     4 1 10 my ( $self, $val ) = @_;
224 4 100 100     15 $self->{'unit'} = $val if $val && ref($val) eq 'ARRAY';
225 4 100       15 return $self->{'unit'} if defined $self->{'unit'};
226 1         3 return undef;
227             }
228              
229             =head2 types()
230              
231             my @types = $node->types();
232              
233             # or
234             my $types = $node->types();
235              
236             This method will return an array or an array ref depending
237             on the calling context of types associated with the $node,
238             undef is returned if there are no types.
239              
240             All types returned are lower case.
241              
242             =cut
243              
244             sub types {
245 40     40 1 482 my $self = shift;
246 40         31 my @types;
247 40 100       69 return undef unless defined $self->{params};
248 39         33 foreach my $key ( sort keys %{ $self->{params} } ) {
  39         134  
249 56         65 my $value = $self->{params}->{$key};
250 56 100 66     219 push @types, lc $key if $value && $value eq 'type';
251             }
252 39 100       115 return wantarray ? @types : \@types;
253             }
254              
255             =head2 is_type()
256              
257             if ( $node->is_type($type) ) {
258              
259             # ...
260             }
261              
262             Given a type (see types() for a list of those set)
263             this method returns 1 if the $node is of that type
264             or undef if it is not.
265              
266             =cut
267              
268             sub is_type {
269 3115     3115 1 2255 my ( $self, $type ) = @_;
270 3115 100 100     7570 if ( defined $self->{params} && exists $self->{params}->{ lc($type) } ) {
271              
272             # Make this always return true so as not to change the net
273             # behaviour of the method. if for some wack (and
274             # non-compliant) reason this value is undef, empty string or
275             # zero, tough luck.
276 317   50     804 return $self->{params}{ lc $type } || 1;
277             }
278 2798         5537 return undef;
279             }
280              
281             =head2 is_pref();
282              
283             if ( $node->is_pref() ) {
284             print "Preferred node";
285             }
286              
287             This method is the same as is_type (which can take a value of 'pref')
288             but it specific to if it is the preferred node. This method is used
289             to sort when returning lists of nodes.
290              
291             =cut
292              
293             # A preferred node can be indicated in a vcard file 2 ways:
294             #
295             # 1. As 'PREF=1' which makes $self->{params} look like:
296             # { 1 => 'pref', work => 'type' }
297             #
298             # 2. As 'TYPE=PREF' which makes $self->{params} look like:
299             # { pref => 'type', work => 'type' }
300             #
301             sub is_pref {
302 39     39 1 32 my $self = shift;
303 39         40 my $params = $self->{params};
304 39 100 66     197 if (( defined $params ) && #
      66        
      100        
305             ( defined $params->{1} && $params->{1} eq 'pref' ) || #
306             ( defined $params->{pref} )
307             )
308             {
309 13         40 return 1;
310             }
311 26         54 return undef;
312             }
313              
314             =head2 add_types()
315              
316             $address->add_types('home');
317              
318             my @types = qw(home work);
319             $address->add_types( \@types );
320              
321             Add a type to an address, it can take a scalar or an array ref.
322              
323             =cut
324              
325             sub add_types {
326 4     4 1 10 my ( $self, $type ) = @_;
327 4 100       9 unless ( defined $self->{params} ) {
328              
329             # no params, create a hash ref in there
330 2         3 my %params;
331 2         3 $self->{params} = \%params;
332             }
333 4 100       11 if ( ref($type) eq 'ARRAY' ) {
334 1         1 map { $self->{params}->{ lc($_) } = 'type' } @{$type};
  2         4  
  1         2  
335             } else {
336 3         9 $self->{params}->{ lc($type) } = 'type';
337             }
338             }
339              
340             =head2 remove_types()
341              
342             $address->remove_types('home');
343              
344             my @types = qw(home work);
345             $address->remove_types( \@types );
346              
347             This method removes a type from an address, it can take a scalar
348             or an array ref.
349              
350             undef is returned when in scalar context and the type does not match,
351             or when in array ref context and none of the types match, true is
352             returned otherwise.
353              
354             =cut
355              
356             sub remove_types {
357 5     5 1 7 my ( $self, $type ) = @_;
358 5 100       13 return undef unless defined $self->{params};
359              
360 4 100       6 if ( ref($type) eq 'ARRAY' ) {
361 2         2 my $to_return = undef;
362 2         2 foreach my $t ( @{$type} ) {
  2         4  
363 3 100       7 if ( exists $self->{params}->{ lc($t) } ) {
364 2         3 delete $self->{params}->{ lc($t) };
365 2         2 $to_return = 1;
366             }
367             }
368 2         4 return $to_return;
369             } else {
370 2 100       6 if ( exists $self->{params}->{ lc($type) } ) {
371 1         1 delete $self->{params}->{ lc($type) };
372 1         2 return 1;
373             }
374             }
375 1         3 return undef;
376             }
377              
378             =head2 group()
379              
380             my $group = $node->group();
381              
382             If called without any arguments, this method returns the group
383             name if a node belongs to a group. Otherwise undef is returned.
384              
385             If an argument is supplied then this is set as the group name.
386              
387             All group names are always lowercased.
388              
389             For example, Apple Address book used 'itemN' to group it's
390             custom X-AB... nodes with a TEL or ADR node.
391              
392             =cut
393              
394             sub group {
395 524     524 1 379 my $self = shift;
396 524 100       686 if ( my $val = shift ) {
397 16         23 $self->{group} = lc($val);
398             }
399 524 100       734 return $self->{group} if defined $self->{group};
400 473         550 return undef;
401             }
402              
403             =head2 export_data()
404              
405             NOTE: This method is deprecated and should not be used. It will be removed in
406             a later version.
407              
408             my $value = $node->export_data();
409              
410             This method returns the value string of a node.
411             It is only needs to be called when exporting the information
412             back out to ensure that it has not been altered.
413              
414             =cut
415              
416             sub export_data {
417 2     2 1 365 my $self = shift;
418             my @lines = map {
419 14 100       17 if ( defined $self->{$_} ) {
420 13 50       15 if ( ref( $self->{$_} ) eq 'ARRAY' ) {
421              
422             # Handle things like org etc which have 'units'
423 0         0 join( ',', @{ $self->{$_} } );
  0         0  
424             } else {
425 13         14 $self->{$_};
426             }
427             } else {
428 1         2 '';
429             }
430 2         3 } @{ $self->{'field_order'} };
  2         5  
431              
432             # Should escape stuff here really, but waiting to see what
433             # T::vfile::asData does
434 2         10 return join( ';', @lines );
435              
436             }
437              
438             =head2 as_string
439              
440             Returns the node as a formatted string.
441              
442             =cut
443              
444             sub _key_as_string {
445 175     175   129 my ($self) = @_;
446              
447 175         134 my $n = '';
448 175 100       218 $n .= $self->group . '.' if $self->group;
449 175         226 $n .= $self->node_type;
450 175 100       228 $n .= $self->_params if $self->_params;
451              
452 175         221 return $n;
453             }
454              
455             # returns a string of params formatted for saving to a vcard file
456             # returns false if there are no params
457             sub _params {
458 257     257   203 my ($self) = @_;
459              
460 257         183 my %t;
461 257         162 for my $t ( sort keys %{ $self->{params} } ) {
  257         711  
462 250         367 my $backwards = uc $self->is_type( lc $t );
463 250   100     699 $t{$backwards} ||= [];
464 250         172 push @{ $t{$backwards} }, lc $t;
  250         435  
465             }
466              
467             $t{CHARSET} = [ lc $self->{encoding_out} ]
468             if $self->{encoding_out} ne 'none'
469 257 0 66     1219 && $self->{encoding_out} !~ /UTF-8/i
      33        
      33        
470             && !$self->is_type('b')
471             && !$self->is_type('base64');
472              
473 257         400 my @params = map { sprintf( '%s=%s', $_, join ',', @{ $t{$_} } ) } #
  226         156  
  226         721  
474             sort keys %t;
475              
476 257 100       897 return @params ? ';' . join( ';', @params ) : undef;
477             }
478              
479             # The vCard RFC requires commas, semicolons, and backslashes to be escaped.
480             # See http://tools.ietf.org/search/rfc6350#section-3.4
481             #
482             # Line breaks which are part of a value and are intended to be seen by humans
483             # must have a value of '\n'.
484             # See http://tools.ietf.org/search/rfc6350#section-4.1
485             #
486             # Line breaks which happen because the RFC requires a line break after 75
487             # characters have a value of '\r\n'. These line breaks are not handled by
488             # this method. See _newline() and
489             # http://tools.ietf.org/search/rfc6350#section-3.2
490             #
491             # Don't escape anything if this is a base64 node. Escaping only applies to
492             # strings not binary values.
493             #
494             # 'perldoc perlport' says using \r\n is wrong and confusing for a few reasons
495             # but mainly because the value of \n is different on different operating
496             # systems. It recommends \x0D\x0A instead.
497             sub _escape {
498 356     356   301 my ( $self, $val ) = @_;
499 356 100 66     365 return $val if ( $self->is_type('b') or $self->is_type('base64') );
500 355         532 $val =~ s/(\x0D\x0A|\x0D|\x0A)/\x0A/g;
501 355         386 $val =~ s/([,;|])/\\$1/g;
502 355         360 return $val;
503             }
504              
505             sub _escape_list {
506 1     1   2 my ( $self, @list ) = @_;
507 1         1 return map { $self->_escape($_) } @list;
  2         3  
508             }
509              
510             # The vCard RFC says new lines must be \r\n
511             # See http://tools.ietf.org/search/rfc6350#section-3.2
512             #
513             # 'perldoc perlport' says using \r\n is wrong and confusing for a few reasons
514             # but mainly because the value of \n is different on different operating
515             # systems. It recommends \x0D\x0A instead.
516             sub _newline {
517 175     175   306 my ($self) = @_;
518 175 100       252 return "\x0D\x0A" if $self->{encoding_out} eq 'none';
519 153         229 return Encode::encode( $self->{encoding_out}, "\x0D\x0A" );
520             }
521              
522             sub _decode_string {
523 175     175   170 my ( $self, $string ) = @_;
524 175 100       351 return $string if $self->{encoding_out} eq 'none';
525 153         256 return Encode::decode( $self->{encoding_out}, $string );
526             }
527              
528             sub _encode_string {
529 337     337   2729 my ( $self, $string ) = @_;
530 337 100       503 return $string if $self->{encoding_out} eq 'none';
531 315         504 return Encode::encode( $self->{encoding_out}, $string );
532             }
533              
534             sub _encode_list {
535 0     0   0 my ( $self, @list ) = @_;
536 0 0       0 return @list if $self->{encoding_out} eq 'none';
537 0         0 return map { $self->_encode_string($_) } @list;
  0         0  
538             }
539              
540             # The vCard RFC says lines should be wrapped (or 'folded') at 75 octets
541             # excluding the line break. The line is continued on the next line with a
542             # space as the first character. See
543             # http://tools.ietf.org/search/rfc6350#section-3.1 for details.
544             #
545             # Note than an octet is 1 byte (8 bits) and is not necessarily equal to 1
546             # character, 1 grapheme, 1 codepoint, or 1 column of output. Actually none of
547             # those things are necessarily equal. See
548             # http://www.perl.com/pub/2012/05/perlunicook-string-length-in-graphemes.html
549             #
550             # MIME::QuotedPrint does line wrapping but it assumes the line length must be
551             # <= 76 chars which doesn't work for us.
552             #
553             # Can't use Unicode::LineBreak because it wraps by counting characters and the
554             # vCard spec wants us to wrap by counting octets.
555             sub _wrap {
556 175     175   188 my ( $self, $key, $value ) = @_;
557              
558             return $self->_wrap_naively( $key, $value )
559 175 100       413 unless $self->{encoding_out} =~ /UTF-8/i;
560              
561 153 100 66     182 if ( $self->is_type('q') or $self->is_type('quoted-printable') ) {
562             ## See the Quoted-Printable RFC205
563             ## https://tools.ietf.org/html/rfc2045#section-6.7 (rule 5)
564 10         16 my $newline
565             = $self->_encode_string("=")
566             . $self->_newline
567             . $self->_encode_string(" ");
568 10         157 my $max
569             = 73; # 75 octets per line max including '=' and ' ' from $newline
570 10         17 return $self->_wrap_utf8( $key, $value, $max, $newline );
571             }
572              
573 143         189 my $newline = $self->_newline . $self->_encode_string(" ");
574 143         2263 my $max = 74; # 75 octets per line max including " " from $newline
575 143         186 return $self->_wrap_utf8( $key, $value, $max, $newline );
576             }
577              
578             sub _wrap_utf8 {
579 153     153   164 my ( $self, $key, $value, $max, $newline ) = @_;
580              
581 153         341 my $gcs = Unicode::GCString->new(Encode::decode('UTF-8', $key . $value));
582 153 100       6043 return $key . $value if bytes::length( $gcs->as_string ) <= $max;
583              
584 11         36 my $start = 0;
585 11         12 my @wrapped_lines;
586              
587             # first line is 1 character longer than the others because it doesn't
588             # begin with a " "
589 11         14 my $first_max = $max + 1;
590              
591 11         41 while ( $start <= $gcs->length ) {
592 47         34 my $len = 1;
593              
594 47         89 while ( ( $start + $len ) <= $gcs->length ) {
595              
596 2840         5257 my $line = $gcs->substr( $start, $len );
597 2840         8016 my $bytes = bytes::length( $line->as_string );
598              
599             # is this a good place to line wrap?
600 2840 100 100     6380 if ( $first_max && $bytes <= $first_max ) {
601             ## no its not a good place to line wrap
602             ## this if statement is only hit on the first line wrap
603 727         411 $len++;
604 727         1845 next;
605             }
606 2113 100       2335 if ( $bytes <= $max ) {
607             ## no its not a good place to line wrap
608 2066         1143 $len++;
609 2066         5075 next;
610             }
611              
612             # wrap the line here
613 47         242 $line = $gcs->substr( $start, $len - 1 )->as_string;
614 47         139 push @wrapped_lines, Encode::encode('UTF-8',$line);
615 47         1010 $start += $len - 1;
616 47         46 last;
617             }
618              
619 47 100       119 if ( ( $start + $len - 1 ) >= $gcs->length ) {
620 11         49 my $line = $gcs->substr( $start, $len - 1 )->as_string;
621 11         33 push @wrapped_lines, Encode::encode('UTF-8',$line);
622 11         189 last;
623             }
624              
625 36         73 $first_max = undef;
626             }
627              
628 11         72 return join $newline, @wrapped_lines;
629             }
630              
631             # This will fail to line wrap properly for wide characters. The problem
632             # is it naively wraps lines by counting the number of characters but the vcard
633             # spec wants us to wrap after 75 octets (bytes). However clever vCard readers
634             # may be able to deal with this.
635             sub _wrap_naively {
636 22     22   20 my ( $self, $key, $value ) = @_;
637              
638 22         18 $Text::Wrap::columns = 75; # wrap after 75 chars
639 22         44 $Text::Wrap::break = qr/[.]/; # allow lines breaks anywhere
640 22         37 $Text::Wrap::separator = $self->_newline; # use encoded new lines
641              
642 22         20 my $first_prefix = $key; # this text is placed before first line
643 22         17 my $prefix = " "; # this text is placed before all other lines
644 22         37 return Text::Wrap::wrap( $first_prefix, $prefix, $value );
645             }
646              
647             sub _encode {
648 175     175   154 my ( $self, $value ) = @_;
649              
650 175 100 66     188 if ( $self->is_type('q') or $self->is_type('quoted-printable') ) {
    100 66        
651              
652             # Encode with Encode::encode()
653 10         19 my $encoded_value = $self->_encode_string($value);
654 10         267 return MIME::QuotedPrint::encode( $encoded_value, '' );
655              
656             } elsif ( $self->is_type('b') or $self->is_type('base64') ) {
657              
658             # Scenarios where MIME::Base64::encode() works:
659             # - for binary data (photo) -- 99% of cases
660             # - if $value is a string with wide characters and the user has
661             # encoded it as UTF-8.
662             # - if $value is a string with no wide characters
663             #
664             # Scenario where MIME::Base64::encode() will die:
665             # - if $value is a string with wide characters and the user has not
666             # encoded it as UTF-8.
667 1         6 return MIME::Base64::encode( $value, '' );
668              
669             } else {
670 164         196 $value = $self->_encode_string($value);
671             }
672              
673 164         3424 return $value;
674             }
675              
676             # This method does the following:
677             # 1. Escape and concatenate values
678             # 2. Encode::encode() values
679             # 3. MIME encode() values
680             # 4. wrap lines to 75 octets
681             # 5. Encode::decode() value
682             #
683             # Assumes there is only one MIME::Quoted-Printable field for a node.
684             # Assumes there is only one MIME::Base64 field for a node.
685             #
686             # If either of the above assumptions is false, line wrapping may be incorrect.
687             # However clever vCard readers may still be able to read vCards with incorrect
688             # line wrapping.
689             sub as_string {
690 175     175 1 147 my ($self) = @_;
691 175         223 my $key = $self->_key_as_string();
692              
693             # Build up $raw_value from field values
694 175         137 my @field_values;
695 175         426 my $field_names = $self->{field_order};
696 175         196 foreach my $field_name (@$field_names) {
697 368 100       1448 next unless defined( my $field_value = $self->{$field_name} );
698              
699             # escape stuff
700 355 100       599 $field_value = ref $field_value eq 'ARRAY' #
701             ? join( ';', $self->_escape_list(@$field_value) )
702             : $self->_escape($field_value);
703              
704 355         426 push @field_values, $field_value;
705             }
706 175         240 my $raw_value = join ';', @field_values;
707              
708             # MIME::*::encode() value
709 175         241 my $encoded = $self->_encode($raw_value);
710              
711             # Line wrap everything to 75 octets
712 175         329 my $wrapped = $self->_wrap( $key . ":", $encoded );
713              
714             # Decode everything
715 175         8399 return $self->_decode_string($wrapped);
716             }
717              
718             # Because we have autoload
719       0     sub DESTROY {
720             }
721              
722             # creates methods for a node object based on the field_names in the config
723             # hash of the node.
724              
725             sub AUTOLOAD {
726 318     318   621 my $name = $AUTOLOAD;
727 318         704 $name =~ s/.*://;
728              
729             carp "$name method which is not valid for this node"
730 318 100       692 unless defined $_[0]->{field_lookup}->{$name};
731              
732 317 100       424 if ( $_[1] ) {
733              
734             # set it
735 39         64 $_[0]->{$name} = $_[1];
736             }
737              
738             # Return it
739 317         914 return $_[0]->{$name};
740             }
741              
742             =head2 NOTES
743              
744             If a node has a param of 'quoted-printable' then the
745             value is escaped (basically converting Hex return into \r\n
746             as far as I can see).
747              
748             =head1 AUTHOR
749              
750             Leo Lapworth, LLAP@cuckoo.org
751             Eric Johnson (kablamo), github ~!at!~ iijo dot org
752              
753             =head1 SEE ALSO
754              
755             L L,
756             L L,
757             L L,
758              
759             =cut
760              
761             1;
762