File Coverage

lib/Text/vCard/Node.pm
Criterion Covered Total %
statement 254 261 97.3
branch 97 102 95.1
condition 45 60 75.0
subroutine 34 36 94.4
pod 11 11 100.0
total 441 470 93.8


line stmt bran cond sub pod time code
1             package Text::vCard::Node;
2             $Text::vCard::Node::VERSION = '3.07';
3 16     16   23709 use strict;
  16         17  
  16         569  
4 16     16   69 use warnings;
  16         18  
  16         416  
5 16     16   65 use Carp;
  16         20  
  16         835  
6 16     16   8518 use Encode;
  16         136519  
  16         1347  
7 16     16   7431 use MIME::Base64 3.07;
  16         9473  
  16         921  
8 16     16   7600 use MIME::QuotedPrint 3.07;
  16         3354  
  16         780  
9 16     16   7148 use Unicode::LineBreak;
  16         222073  
  16         1048  
10 16     16   9545 use Text::Wrap;
  16         41309  
  16         949  
11 16     16   101 use vars qw ( $AUTOLOAD );
  16         24  
  16         43020  
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 321     321 1 2522 my ( $proto, $conf ) = @_;
70 321   100     921 my $class = ref($proto) || $proto;
71 321         356 my $self = {};
72 321 100       757 carp "No fields defined" unless defined $conf->{'fields'};
73 320 100       744 carp "fields is not an array ref"
74             unless ref( $conf->{'fields'} ) eq 'ARRAY';
75              
76 319         559 bless( $self, $class );
77              
78 318   100     744 $self->{encoding_out} = $conf->{encoding_out} || 'UTF-8';
79              
80 318 100       793 $self->{node_type} = uc( $conf->{node_type} )
81             if defined $conf->{node_type};
82 318 100       826 $self->group( $conf->{group} ) if defined $conf->{group};
83              
84             # Store the field order.
85 318         382 $self->{'field_order'} = $conf->{'fields'};
86              
87             # store the actual field names so we can look them up
88 318         249 my %fields;
89 318         248 map { $fields{$_} = 1 } @{ $self->{'field_order'} };
  756         1152  
  318         446  
90 318         451 $self->{'field_lookup'} = \%fields;
91              
92 318 100       591 if ( defined $conf->{'data'} ) {
93              
94             # Populate now, rather than later (via AUTOLOAD)
95             # store values into object
96 315 100       556 if ( defined $conf->{'data'}->{'params'} ) {
97 139         182 my %params;
98              
99             # Loop through array
100 139         163 foreach my $param_hash ( @{ $conf->{'data'}->{'params'} } ) {
  139         232  
101 192         175 while ( my ( $key, $value ) = each %{$param_hash} ) {
  384         1069  
102 192         168 my $t = 'type';
103              
104             # go through each key/value pair
105 192         161 my $param_list = $key;
106 192 100       291 if ( defined $value ) {
107 175         181 $t = $key;
108              
109             # use value, not key as its 'type' => 'CELL',
110             # not 'CELL' => undef
111 175         183 $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         499 for my $p ( split /\s*,\s*/, $param_list ) {
118 202         1125 $p =~ s/^\s*(.*?)\s*$/\L$1/;
119 202         357 $p =~ s/\s+/ /g;
120 202         665 $params{$p} = lc $t;
121             }
122             }
123             }
124 139         261 $self->{params} = \%params;
125             }
126              
127 315 100       605 if ( defined $conf->{'data'}->{'value'} ) {
128              
129             # Store the actual data into the object
130              
131 279 100 66     533 if ( $self->is_type('q') or $self->is_type('quoted-printable') ) {
132              
133 9         27 my $value = $conf->{data}{value};
134 9         80 my $mime_decoded = MIME::QuotedPrint::decode($value);
135 9         33 my $encode_decoded = Encode::decode( 'UTF-8', $mime_decoded );
136 9         336 my $unescaped = $self->_unescape($encode_decoded);
137 9         21 $conf->{'data'}->{'value'} = $unescaped;
138             }
139              
140 279 100 66     378 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         4 my $value = $conf->{data}{value};
149 1         26 my $mime_decoded = MIME::Base64::decode($value);
150 1         4 $conf->{data}{value} = $mime_decoded;
151              
152             # mimic what goes on below
153 1         3 @{$self}{ @{ $self->{field_order} } }
  1         4  
  1         2  
154             = ( $conf->{data}{value} );
155             } else {
156              
157             # the -1 on split is so ;; values create elements in
158             # the array
159 278         1189 my @elements = split /(?{data}{value}, -1;
160 278 100 100     1059 if ( defined $self->{node_type}
    100          
161             && $self->{node_type} eq 'ORG' )
162 274         499 {
163 4         13 my @unescaped = $self->_unescape_list(@elements);
164              
165 4         11 $self->{'name'} = shift(@unescaped);
166 4 100       23 $self->{'unit'} = \@unescaped if scalar(@unescaped) > 0;
167             }
168              
169             # no need for explicit scalar
170             elsif ( @elements <= @{ $self->{field_order} } ) {
171 273         472 my @unescaped = $self->_unescape_list(@elements);
172              
173             # set the field values as the data
174             # e.g. $self->{street} = 'The street'
175 273         283 @{$self}{ @{ $self->{field_order} } } = @unescaped;
  273         787  
  273         299  
176             } else {
177 1         84 carp sprintf(
178             'Data value had %d elements expecting %d or less.',
179             scalar @elements,
180 1         1 scalar @{ $self->{field_order} }
181             );
182             }
183             }
184             }
185             }
186 317         726 return $self;
187             }
188              
189             sub _unescape {
190 692     692   560 my ( $self, $value ) = @_;
191 692         786 $value =~ s|\\([\\,;])|$1|g;
192 692         1209 return $value;
193             }
194              
195             sub _unescape_list {
196 277     277   464 my ( $self, @values ) = @_;
197 277         316 return map { $self->_unescape($_) } @values;
  683         846  
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 173     173 1 324 $_[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 12 my ( $self, $val ) = @_;
224 4 100 100     24 $self->{'unit'} = $val if $val && ref($val) eq 'ARRAY';
225 4 100       22 return $self->{'unit'} if defined $self->{'unit'};
226 1         4 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 526 my $self = shift;
246 40         34 my @types;
247 40 100       80 return undef unless defined $self->{params};
248 39         37 foreach my $key ( sort keys %{ $self->{params} } ) {
  39         140  
249 56         82 my $value = $self->{params}->{$key};
250 56 100 66     245 push @types, lc $key if $value && $value eq 'type';
251             }
252 39 100       146 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 3095     3095 1 2624 my ( $self, $type ) = @_;
270 3095 100 100     9038 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     949 return $self->{params}{ lc $type } || 1;
277             }
278 2778         6239 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 41 my $self = shift;
303 39         37 my $params = $self->{params};
304 39 100 66     240 if (( defined $params ) && #
      66        
      100        
305             ( defined $params->{1} && $params->{1} eq 'pref' ) || #
306             ( defined $params->{pref} )
307             )
308             {
309 13         53 return 1;
310             }
311 26         68 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 11 my ( $self, $type ) = @_;
327 4 100       14 unless ( defined $self->{params} ) {
328              
329             # no params, create a hash ref in there
330 2         2 my %params;
331 2         5 $self->{params} = \%params;
332             }
333 4 100       10 if ( ref($type) eq 'ARRAY' ) {
334 1         2 map { $self->{params}->{ lc($_) } = 'type' } @{$type};
  2         16  
  1         2  
335             } else {
336 3         11 $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       18 return undef unless defined $self->{params};
359              
360 4 100       10 if ( ref($type) eq 'ARRAY' ) {
361 2         3 my $to_return = undef;
362 2         2 foreach my $t ( @{$type} ) {
  2         4  
363 3 100       8 if ( exists $self->{params}->{ lc($t) } ) {
364 2         5 delete $self->{params}->{ lc($t) };
365 2         3 $to_return = 1;
366             }
367             }
368 2         6 return $to_return;
369             } else {
370 2 100       7 if ( exists $self->{params}->{ lc($type) } ) {
371 1         3 delete $self->{params}->{ lc($type) };
372 1         2 return 1;
373             }
374             }
375 1         4 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 521     521 1 462 my $self = shift;
396 521 100       780 if ( my $val = shift ) {
397 16         27 $self->{group} = lc($val);
398             }
399 521 100       899 return $self->{group} if defined $self->{group};
400 470         628 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 446 my $self = shift;
418             my @lines = map {
419 14 100       24 if ( defined $self->{$_} ) {
  2         5  
420 13 50       19 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         22 $self->{$_};
426             }
427             } else {
428 1         2 '';
429             }
430 2         3 } @{ $self->{'field_order'} };
431              
432             # Should escape stuff here really, but waiting to see what
433             # T::vfile::asData does
434 2         13 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 173     173   147 my ($self) = @_;
446              
447 173         159 my $n = '';
448 173 100       221 $n .= $self->group . '.' if $self->group;
449 173         251 $n .= $self->node_type;
450 173 100       263 $n .= $self->_params if $self->_params;
451              
452 173         280 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 255     255   230 my ($self) = @_;
459              
460 255         195 my %t;
461 255         197 for my $t ( sort keys %{ $self->{params} } ) {
  255         757  
462 250         405 my $backwards = uc $self->is_type( lc $t );
463 250   100     791 $t{$backwards} ||= [];
464 250         179 push @{ $t{$backwards} }, lc $t;
  250         514  
465             }
466              
467 255 0 66     1459 $t{CHARSET} = [ lc $self->{encoding_out} ]
      33        
      33        
468             if $self->{encoding_out} ne 'none'
469             && $self->{encoding_out} !~ /UTF-8/i
470             && !$self->is_type('b')
471             && !$self->is_type('base64');
472              
473 255         406 my @params = map { sprintf( '%s=%s', $_, join ',', @{ $t{$_} } ) } #
  226         173  
  226         749  
474             sort keys %t;
475              
476 255 100       956 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 354     354   325 my ( $self, $val ) = @_;
499 354 100 66     401 return $val if ( $self->is_type('b') or $self->is_type('base64') );
500 353         569 $val =~ s/(\x0D\x0A|\x0D|\x0A)/\x0A/g;
501 353         445 $val =~ s/([,;|])/\\$1/g;
502 353         406 return $val;
503             }
504              
505             sub _escape_list {
506 1     1   4 my ( $self, @list ) = @_;
507 1         3 return map { $self->_escape($_) } @list;
  2         6  
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 173     173   377 my ($self) = @_;
518 173 100       295 return "\x0D\x0A" if $self->{encoding_out} eq 'none';
519 151         288 return Encode::encode( $self->{encoding_out}, "\x0D\x0A" );
520             }
521              
522             sub _decode_string {
523 173     173   176 my ( $self, $string ) = @_;
524 173 100       401 return $string if $self->{encoding_out} eq 'none';
525 151         298 return Encode::decode( $self->{encoding_out}, $string );
526             }
527              
528             sub _encode_string {
529 333     333   3044 my ( $self, $string ) = @_;
530 333 100       580 return $string if $self->{encoding_out} eq 'none';
531 311         573 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 173     173   192 my ( $self, $key, $value ) = @_;
557              
558 173 100       495 return $self->_wrap_naively( $key, $value )
559             unless $self->{encoding_out} =~ /UTF-8/i;
560              
561 151 100 66     206 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         17 my $newline
565             = $self->_encode_string("=")
566             . $self->_newline
567             . $self->_encode_string(" ");
568 10         194 my $max
569             = 73; # 75 octets per line max including '=' and ' ' from $newline
570 10         19 return $self->_wrap_utf8( $key, $value, $max, $newline );
571             }
572              
573 141         213 my $newline = $self->_newline . $self->_encode_string(" ");
574 141         2485 my $max = 74; # 75 octets per line max including " " from $newline
575 141         224 return $self->_wrap_utf8( $key, $value, $max, $newline );
576             }
577              
578             sub _wrap_utf8 {
579 151     151   180 my ( $self, $key, $value, $max, $newline ) = @_;
580              
581 151         520 my $gcs = Unicode::GCString->new( $key . $value );
582 151 100       3276 return $key . $value if $gcs->length <= $max;
583              
584 9         16 my $start = 0;
585 9         11 my @wrapped_lines;
586              
587             # first line is 1 character longer than the others because it doesn't
588             # begin with a " "
589 9         14 my $first_max = $max + 1;
590              
591 9         38 while ( $start <= $gcs->length ) {
592 33         36 my $len = 1;
593              
594 33         99 while ( ( $start + $len ) <= $gcs->length ) {
595              
596 2480         8897 my $line = $gcs->substr( $start, $len );
597 2480         12396 my $bytes = bytes::length( $line->as_string );
598              
599             # is this a good place to line wrap?
600 2480 100 100     12250 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 671         477 $len++;
604 671         2055 next;
605             }
606 1809 100       3359 if ( $bytes <= $max ) {
607             ## no its not a good place to line wrap
608 1776         1437 $len++;
609 1776         8338 next;
610             }
611              
612             # wrap the line here
613 33         375 $line = $gcs->substr( $start, $len - 1 )->as_string;
614 33         164 push @wrapped_lines, $line;
615 33         44 $start += $len - 1;
616 33         47 last;
617             }
618              
619 33 100       110 if ( ( $start + $len - 1 ) >= $gcs->length ) {
620 9         53 my $line = $gcs->substr( $start, $len - 1 )->as_string;
621 9         26 push @wrapped_lines, $line;
622 9         15 last;
623             }
624              
625 24         78 $first_max = undef;
626             }
627              
628 9         87 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   22 my ( $self, $key, $value ) = @_;
637              
638 22         23 $Text::Wrap::columns = 75; # wrap after 75 chars
639 22         66 $Text::Wrap::break = qr/[.]/; # allow lines breaks anywhere
640 22         48 $Text::Wrap::separator = $self->_newline; # use encoded new lines
641              
642 22         21 my $first_prefix = $key; # this text is placed before first line
643 22         20 my $prefix = " "; # this text is placed before all other lines
644 22         43 return Text::Wrap::wrap( $first_prefix, $prefix, $value );
645             }
646              
647             sub _encode {
648 173     173   166 my ( $self, $value ) = @_;
649              
650 173 100 66     208 if ( $self->is_type('q') or $self->is_type('quoted-printable') ) {
    100 66        
651              
652             # Encode with Encode::encode()
653 10         22 my $encoded_value = $self->_encode_string($value);
654 10         275 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         13 return MIME::Base64::encode( $value, '' );
668              
669             } else {
670 162         233 $value = $self->_encode_string($value);
671             }
672              
673 162         3717 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 173     173 1 147 my ($self) = @_;
691 173         268 my $key = $self->_key_as_string();
692              
693             # Build up $raw_value from field values
694 173         132 my @field_values;
695 173         175 my $field_names = $self->{field_order};
696 173         206 foreach my $field_name (@$field_names) {
697 366 100       683 next unless defined( my $field_value = $self->{$field_name} );
698              
699             # escape stuff
700 353 100       643 $field_value = ref $field_value eq 'ARRAY' #
701             ? join( ';', $self->_escape_list(@$field_value) )
702             : $self->_escape($field_value);
703              
704 353         493 push @field_values, $field_value;
705             }
706 173         280 my $raw_value = join ';', @field_values;
707              
708             # MIME::*::encode() value
709 173         255 my $encoded = $self->_encode($raw_value);
710              
711             # Line wrap everything to 75 octets
712 173         384 my $wrapped = $self->_wrap( $key . ":", $encoded );
713              
714             # Decode everything
715 173         3285 return $self->_decode_string($wrapped);
716             }
717              
718             # Because we have autoload
719 0     0   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   901 my $name = $AUTOLOAD;
727 318         888 $name =~ s/.*://;
728              
729 318 100       910 carp "$name method which is not valid for this node"
730             unless defined $_[0]->{field_lookup}->{$name};
731              
732 317 100       433 if ( $_[1] ) {
733              
734             # set it
735 39         74 $_[0]->{$name} = $_[1];
736             }
737              
738             # Return it
739 317         1125 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