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.08';
3 16     16   59331 use strict;
  16         16  
  16         337  
4 16     16   43 use warnings;
  16         24  
  16         269  
5 16     16   43 use Carp;
  16         16  
  16         631  
6 16     16   6941 use Encode;
  16         99113  
  16         981  
7 16     16   5797 use MIME::Base64 3.07;
  16         6821  
  16         841  
8 16     16   5821 use MIME::QuotedPrint 3.07;
  16         2766  
  16         740  
9 16     16   5751 use Unicode::LineBreak;
  16         162033  
  16         649  
10 16     16   6789 use Text::Wrap;
  16         30906  
  16         768  
11 16     16   77 use vars qw ( $AUTOLOAD );
  16         18  
  16         34504  
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 2667 my ( $proto, $conf ) = @_;
70 322   100     741 my $class = ref($proto) || $proto;
71 322         257 my $self = {};
72 322 100       620 carp "No fields defined" unless defined $conf->{'fields'};
73             carp "fields is not an array ref"
74 321 100       602 unless ref( $conf->{'fields'} ) eq 'ARRAY';
75              
76 320         325 bless( $self, $class );
77              
78 319   100     575 $self->{encoding_out} = $conf->{encoding_out} || 'UTF-8';
79              
80             $self->{node_type} = uc( $conf->{node_type} )
81 319 100       627 if defined $conf->{node_type};
82 319 100       636 $self->group( $conf->{group} ) if defined $conf->{group};
83              
84             # Store the field order.
85 319         288 $self->{'field_order'} = $conf->{'fields'};
86              
87             # store the actual field names so we can look them up
88 319         226 my %fields;
89 319         232 map { $fields{$_} = 1 } @{ $self->{'field_order'} };
  757         857  
  319         381  
90 319         313 $self->{'field_lookup'} = \%fields;
91              
92 319 100       457 if ( defined $conf->{'data'} ) {
93              
94             # Populate now, rather than later (via AUTOLOAD)
95             # store values into object
96 316 100       429 if ( defined $conf->{'data'}->{'params'} ) {
97 139         122 my %params;
98              
99             # Loop through array
100 139         166 foreach my $param_hash ( @{ $conf->{'data'}->{'params'} } ) {
  139         211  
101 192         138 while ( my ( $key, $value ) = each %{$param_hash} ) {
  384         968  
102 192         139 my $t = 'type';
103              
104             # go through each key/value pair
105 192         169 my $param_list = $key;
106 192 100       245 if ( defined $value ) {
107 175         116 $t = $key;
108              
109             # use value, not key as its 'type' => 'CELL',
110             # not 'CELL' => undef
111 175         142 $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         394 for my $p ( split /\s*,\s*/, $param_list ) {
118 202         1010 $p =~ s/^\s*(.*?)\s*$/\L$1/;
119 202         255 $p =~ s/\s+/ /g;
120 202         543 $params{$p} = lc $t;
121             }
122             }
123             }
124 139         182 $self->{params} = \%params;
125             }
126              
127 316 100       449 if ( defined $conf->{'data'}->{'value'} ) {
128              
129             # Store the actual data into the object
130              
131 280 100 66     396 if ( $self->is_type('q') or $self->is_type('quoted-printable') ) {
132              
133 9         12 my $value = $conf->{data}{value};
134 9         81 my $mime_decoded = MIME::QuotedPrint::decode($value);
135 9         30 my $encode_decoded = Encode::decode( 'UTF-8', $mime_decoded );
136 9         298 my $unescaped = $self->_unescape($encode_decoded);
137 9         18 $conf->{'data'}->{'value'} = $unescaped;
138             }
139              
140 280 100 66     311 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         2 my $value = $conf->{data}{value};
149 1         15 my $mime_decoded = MIME::Base64::decode($value);
150 1         3 $conf->{data}{value} = $mime_decoded;
151              
152             # mimic what goes on below
153 1         3 @{$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         861 my @elements = split /(?{data}{value}, -1;
160 279 100 100     835 if ( defined $self->{node_type}
    100          
161             && $self->{node_type} eq 'ORG' )
162             {
163 4         11 my @unescaped = $self->_unescape_list(@elements);
164              
165 4         7 $self->{'name'} = shift(@unescaped);
166 4 100       13 $self->{'unit'} = \@unescaped if scalar(@unescaped) > 0;
167             }
168              
169             # no need for explicit scalar
170 275         402 elsif ( @elements <= @{ $self->{field_order} } ) {
171 274         378 my @unescaped = $self->_unescape_list(@elements);
172              
173             # set the field values as the data
174             # e.g. $self->{street} = 'The street'
175 274         261 @{$self}{ @{ $self->{field_order} } } = @unescaped;
  274         644  
  274         252  
176             } else {
177             carp sprintf(
178             'Data value had %d elements expecting %d or less.',
179             scalar @elements,
180 1         1 scalar @{ $self->{field_order} }
  1         80  
181             );
182             }
183             }
184             }
185             }
186 318         587 return $self;
187             }
188              
189             sub _unescape {
190 693     693   505 my ( $self, $value ) = @_;
191 693         591 $value =~ s|\\([\\,;])|$1|g;
192 693         1051 return $value;
193             }
194              
195             sub _unescape_list {
196 278     278   363 my ( $self, @values ) = @_;
197 278         239 return map { $self->_unescape($_) } @values;
  684         697  
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 239 $_[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     17 $self->{'unit'} = $val if $val && ref($val) eq 'ARRAY';
225 4 100       16 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 505 my $self = shift;
246 40         28 my @types;
247 40 100       67 return undef unless defined $self->{params};
248 39         27 foreach my $key ( sort keys %{ $self->{params} } ) {
  39         124  
249 56         61 my $value = $self->{params}->{$key};
250 56 100 66     236 push @types, lc $key if $value && $value eq 'type';
251             }
252 39 100       108 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 2219 my ( $self, $type ) = @_;
270 3115 100 100     7420 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     795 return $self->{params}{ lc $type } || 1;
277             }
278 2798         6160 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 27 my $self = shift;
303 39         32 my $params = $self->{params};
304 39 100 66     177 if (( defined $params ) && #
      66        
      100        
305             ( defined $params->{1} && $params->{1} eq 'pref' ) || #
306             ( defined $params->{pref} )
307             )
308             {
309 13         34 return 1;
310             }
311 26         50 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       10 unless ( defined $self->{params} ) {
328              
329             # no params, create a hash ref in there
330 2         3 my %params;
331 2         5 $self->{params} = \%params;
332             }
333 4 100       9 if ( ref($type) eq 'ARRAY' ) {
334 1         1 map { $self->{params}->{ lc($_) } = 'type' } @{$type};
  2         5  
  1         2  
335             } else {
336 3         8 $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 8 my ( $self, $type ) = @_;
358 5 100       17 return undef unless defined $self->{params};
359              
360 4 100       10 if ( ref($type) eq 'ARRAY' ) {
361 2         2 my $to_return = undef;
362 2         3 foreach my $t ( @{$type} ) {
  2         5  
363 3 100       7 if ( exists $self->{params}->{ lc($t) } ) {
364 2         6 delete $self->{params}->{ lc($t) };
365 2         3 $to_return = 1;
366             }
367             }
368 2         7 return $to_return;
369             } else {
370 2 100       6 if ( exists $self->{params}->{ lc($type) } ) {
371 1         2 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 417 my $self = shift;
396 524 100       672 if ( my $val = shift ) {
397 16         23 $self->{group} = lc($val);
398             }
399 524 100       769 return $self->{group} if defined $self->{group};
400 473         515 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 344 my $self = shift;
418             my @lines = map {
419 14 100       18 if ( defined $self->{$_} ) {
420 13 50       16 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         4  
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       217 $n .= $self->group . '.' if $self->group;
449 175         225 $n .= $self->node_type;
450 175 100       256 $n .= $self->_params if $self->_params;
451              
452 175         233 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   182 my ($self) = @_;
459              
460 257         174 my %t;
461 257         178 for my $t ( sort keys %{ $self->{params} } ) {
  257         656  
462 250         380 my $backwards = uc $self->is_type( lc $t );
463 250   100     728 $t{$backwards} ||= [];
464 250         184 push @{ $t{$backwards} }, lc $t;
  250         431  
465             }
466              
467             $t{CHARSET} = [ lc $self->{encoding_out} ]
468             if $self->{encoding_out} ne 'none'
469 257 0 66     1153 && $self->{encoding_out} !~ /UTF-8/i
      33        
      33        
470             && !$self->is_type('b')
471             && !$self->is_type('base64');
472              
473 257         373 my @params = map { sprintf( '%s=%s', $_, join ',', @{ $t{$_} } ) } #
  226         170  
  226         707  
474             sort keys %t;
475              
476 257 100       855 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   277 my ( $self, $val ) = @_;
499 356 100 66     354 return $val if ( $self->is_type('b') or $self->is_type('base64') );
500 355         522 $val =~ s/(\x0D\x0A|\x0D|\x0A)/\x0A/g;
501 355         386 $val =~ s/([,;|])/\\$1/g;
502 355         363 return $val;
503             }
504              
505             sub _escape_list {
506 1     1   2 my ( $self, @list ) = @_;
507 1         2 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   298 my ($self) = @_;
518 175 100       283 return "\x0D\x0A" if $self->{encoding_out} eq 'none';
519 153         235 return Encode::encode( $self->{encoding_out}, "\x0D\x0A" );
520             }
521              
522             sub _decode_string {
523 175     175   169 my ( $self, $string ) = @_;
524 175 100       359 return $string if $self->{encoding_out} eq 'none';
525 153         230 return Encode::decode( $self->{encoding_out}, $string );
526             }
527              
528             sub _encode_string {
529 337     337   2703 my ( $self, $string ) = @_;
530 337 100       499 return $string if $self->{encoding_out} eq 'none';
531 315         489 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   181 my ( $self, $key, $value ) = @_;
557              
558             return $self->_wrap_naively( $key, $value )
559 175 100       405 unless $self->{encoding_out} =~ /UTF-8/i;
560              
561 153 100 66     188 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         159 my $max
569             = 73; # 75 octets per line max including '=' and ' ' from $newline
570 10         15 return $self->_wrap_utf8( $key, $value, $max, $newline );
571             }
572              
573 143         211 my $newline = $self->_newline . $self->_encode_string(" ");
574 143         2619 my $max = 74; # 75 octets per line max including " " from $newline
575 143         230 return $self->_wrap_utf8( $key, $value, $max, $newline );
576             }
577              
578             sub _wrap_utf8 {
579 153     153   167 my ( $self, $key, $value, $max, $newline ) = @_;
580              
581 153         338 my $gcs = Unicode::GCString->new(Encode::decode('UTF-8', $key . $value));
582 153 100       5884 return $key . $value if bytes::length( $gcs->as_string ) <= $max;
583              
584 11         32 my $start = 0;
585 11         10 my @wrapped_lines;
586              
587             # first line is 1 character longer than the others because it doesn't
588             # begin with a " "
589 11         16 my $first_max = $max + 1;
590              
591 11         39 while ( $start <= $gcs->length ) {
592 47         39 my $len = 1;
593              
594 47         93 while ( ( $start + $len ) <= $gcs->length ) {
595              
596 2840         5236 my $line = $gcs->substr( $start, $len );
597 2840         7939 my $bytes = bytes::length( $line->as_string );
598              
599             # is this a good place to line wrap?
600 2840 100 100     6322 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         404 $len++;
604 727         1803 next;
605             }
606 2113 100       2295 if ( $bytes <= $max ) {
607             ## no its not a good place to line wrap
608 2066         1082 $len++;
609 2066         4957 next;
610             }
611              
612             # wrap the line here
613 47         257 $line = $gcs->substr( $start, $len - 1 )->as_string;
614 47         145 push @wrapped_lines, Encode::encode('UTF-8',$line);
615 47         1028 $start += $len - 1;
616 47         42 last;
617             }
618              
619 47 100       115 if ( ( $start + $len - 1 ) >= $gcs->length ) {
620 11         51 my $line = $gcs->substr( $start, $len - 1 )->as_string;
621 11         33 push @wrapped_lines, Encode::encode('UTF-8',$line);
622 11         194 last;
623             }
624              
625 36         64 $first_max = undef;
626             }
627              
628 11         69 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   19 my ( $self, $key, $value ) = @_;
637              
638 22         19 $Text::Wrap::columns = 75; # wrap after 75 chars
639 22         44 $Text::Wrap::break = qr/[.]/; # allow lines breaks anywhere
640 22         39 $Text::Wrap::separator = $self->_newline; # use encoded new lines
641              
642 22         17 my $first_prefix = $key; # this text is placed before first line
643 22         19 my $prefix = " "; # this text is placed before all other lines
644 22         41 return Text::Wrap::wrap( $first_prefix, $prefix, $value );
645             }
646              
647             sub _encode {
648 175     175   159 my ( $self, $value ) = @_;
649              
650 175 100 66     172 if ( $self->is_type('q') or $self->is_type('quoted-printable') ) {
    100 66        
651              
652             # Encode with Encode::encode()
653 10         16 my $encoded_value = $self->_encode_string($value);
654 10         252 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         199 $value = $self->_encode_string($value);
671             }
672              
673 164         3331 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 135 my ($self) = @_;
691 175         211 my $key = $self->_key_as_string();
692              
693             # Build up $raw_value from field values
694 175         134 my @field_values;
695 175         151 my $field_names = $self->{field_order};
696 175         186 foreach my $field_name (@$field_names) {
697 368 100       549 next unless defined( my $field_value = $self->{$field_name} );
698              
699             # escape stuff
700 355 100       593 $field_value = ref $field_value eq 'ARRAY' #
701             ? join( ';', $self->_escape_list(@$field_value) )
702             : $self->_escape($field_value);
703              
704 355         466 push @field_values, $field_value;
705             }
706 175         235 my $raw_value = join ';', @field_values;
707              
708             # MIME::*::encode() value
709 175         219 my $encoded = $self->_encode($raw_value);
710              
711             # Line wrap everything to 75 octets
712 175         335 my $wrapped = $self->_wrap( $key . ":", $encoded );
713              
714             # Decode everything
715 175         8166 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   602 my $name = $AUTOLOAD;
727 318         690 $name =~ s/.*://;
728              
729             carp "$name method which is not valid for this node"
730 318 100       616 unless defined $_[0]->{field_lookup}->{$name};
731              
732 317 100       360 if ( $_[1] ) {
733              
734             # set it
735 39         117 $_[0]->{$name} = $_[1];
736             }
737              
738             # Return it
739 317         866 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