File Coverage

blib/lib/Mail/Addressbook/Convert/Pegasus.pm
Criterion Covered Total %
statement 176 203 86.7
branch 41 60 68.3
condition 1 3 33.3
subroutine 9 9 100.0
pod 3 3 100.0
total 230 278 82.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Mail::Addressbook::Convert::Pegasus - convert to and from Pegasus addressbooks
4              
5             =head1 SYNOPSIS
6              
7             use strict;
8              
9             use Mail::Addressbook::Convert::Pegasus;
10              
11             my $Pegasus = new Mail::Addressbook::Convert::Pegasus();
12              
13             my $PegasusMain1File ="PegasusMainSample.txt"; # name of a file containing the Pegasus Addressbook data
14              
15             my $PegasusMain2File ="PegasusMainSample.txt"; # name of a file containing the Pegasus Addressbook data
16              
17             my $PegasusDist1InFile ="PegasusDist1Sample.txt"; # name of a the file containing the a distribution list data
18              
19             my $PegasusDist2InFile ="PegasusDist2Sample.txt"; # name of a the file containing the a distribution list data
20              
21              
22             # Convert Pegasus to Standard Intermediate format
23              
24             # see documentation for details on format.
25              
26             my $raIntermediate = $Pegasus->scan([\$PegasusMain1File ,\PegasusMain2File],
27             [\$PegasusDist1InFile, \$PegasusDist2InFile] );
28              
29              
30             # This will also work
31              
32             #my @PegasusMain1Array = @arrayContainingThePegasusMainAddressesData;
33             #my @PegasusMain2Array = @arrayContainingThePegasusMainAddressesData;
34             #my @PegasusDist1Array = @arrayContainingAPegasusDistribution1ListData;
35             #my @PegasusDist2Array = @arrayContainingAPegasusDistribution2ListData;
36             #my @DistNames = qw(Dist1, Dist2);
37              
38             # The third parameter contains the names of the distribution lists that are in the second parameter.
39             # This parameter is only needed is the lists specified as references to arrays.
40             # if they are specified as files, the third parameter is not necessary.
41              
42              
43              
44             #my $raIntermediate = $Pegasus->scan([\@PegasusMain1Array,\@PegasusMain2Array],
45             # [\@PegasusDist1Array , \@PegasusDist2Array],\@DistNames );
46              
47             #( You may put as many distribution lists arrays in the parameters as you wish. )
48              
49              
50             # Convert back to Pegaus
51              
52             my @PegaOut = $Pegasus->output($raIntermediate);
53              
54             #See below for explaination of the output array, and sample code.
55              
56              
57             =head1 REQUIRES
58              
59             Perl, version 5.001 or higher
60              
61             Carp
62             File::Basename;
63              
64              
65              
66             =head1 DESCRIPTION
67              
68             This module is meant to be used as part of the Mail::Addressbook::Convert distribution.
69              
70             It can convert a Pegasus addressbook to a Standard Intermediate format(STF) and a STF to Ldif
71             As part of the larger distribution, it will allow conversion between Pegasus and many other
72             formats.
73              
74             To use to convert between Pegaus and Eudora as an example, you would do the following
75              
76             use Mail::Addressbook::Convert::Pegasus;
77              
78             use Mail::Addressbook::Convert::Eudora;
79              
80             my $Pegasus = new Mail::Addressbook::Convert::Pegasus();
81              
82             my $Eudora = new Mail::Addressbook::Convert::Eudora();
83              
84             # The main addressbooks must be exported from Pegasus in tagged text format.
85             my $PegasusAddr1InFile ="PegasusAddr1Sample.txt"; # name of a file containing Pegasus Tagged text Addressbook data
86              
87             my $PegasusAddr2InFile ="PegasusAddr2Sample.txt"; # name of a file containing Pegasus Tagged text Addressbook data
88              
89             my $PegasusDist1InFile ="PegasusDist1Sample.txt"; # name of the file containing the a distribution list data
90              
91             my $PegasusDist2InFile ="PegasusDist2Sample.txt"; # name of the file containing the a distribution list data
92              
93             my $raIntermediate = $Pegasus->scan( [\$PegasusAddr1InFile, \$PegasusAddr2InFile],
94             [\$PegasusDist1InFile, \$PegasusDist2InFile ]);
95             # $raIntermediate is the intermediate (STF) format file described below.
96              
97              
98             my $raEudora = $Eudora->output($raIntermediate); # reference to an array containing a Eudora addressbook
99              
100             ##------------------------------------------------------------------------
101              
102             The following code will convert from STF intermediate format and write out Pegasus files
103              
104              
105             # $raIntermediate is a reference to an intermediate STF file.
106             my @raPegasus = $Pegasus->output($raIntermediate);
107              
108              
109             my @mainAddressbook = @{$raPegasus[0]};
110             # This array is in tagged text format and must be imported into Pegasus
111              
112              
113             open FH , ">PegasusMainAddressBook" or die "Cannot open PegasusMainAddressBook for writing $!";
114             # This file is in tagged text format and must be imported into Pegasus
115             foreach (@mainAddressbook)
116             {
117             print FH $_;
118             }
119              
120             close FH;
121              
122             my @distListArrayRefs = @{$raPegasus[1]};
123             my $numberOfDistLists = @distListArrayRefs; # an array called in scalar context returns the number
124             #of elements.
125             my @distListArrayNames = @{$raPegasus[2]};
126              
127             foreach my $i (0..$numberOfDistLists-1)
128             {
129             my $DistListName = $distListArrayNames[$i];
130             my @DistList = @{$distListArrayRefs[$i]};
131             open FH , ">$DistListName" or die "Cannot open $DistListName $!";
132             # Thes files are distribution lists and can be used directly, no conversion required
133             foreach (@DistList)
134             {
135             print FH $_;
136             }
137            
138             close FH;
139              
140            
141             }
142              
143              
144              
145             =head1 DEFINITIONS
146            
147             Standard Intermediate Format(STF) :
148              
149             The addressbook format that is used as an intermediate
150             between conversions. It is rfc822 compliant and can
151             be used directly as a Eudora addressbook. Do not use
152             a Eudora addressbook as an STF. Some versions of
153             Eudora use a format, that while RFC822 compliant, will
154             not work as an STF. Run the Eudora addressbook
155             through $Eudora->scan()
156            
157             Pegasus addressbook:
158             Pegausus stores its addresses in multiple files. There are one or
159             more addressbooks, and zero or more distribution lists. Each distribution
160             list is in a separate file.
161            
162             The addressbooks cannot be used directly, but must be exported. Open the
163             addressbook, then use the Addressbook Menu : "Export to Tagged Text File". The
164             exported file(s) will be used as imput.
165            
166             The distribution lists are kept in files with a ".pml" extension. They
167             are used as input directly -- no exporting is necessary. Pegasus does not check
168             for circular references until the distribution list is used. Be sure that you have
169             either used the list, or you have checked that there are no circular references.
170            
171              
172             =head1 METHODS
173              
174             =head2 new
175              
176             no arguments needed.
177              
178             =head2 scan
179              
180             Input :
181              
182             Parameter 1; Required: An anonymous array. Each element of the array is either
183             a reference to an array containing the contents of a tagged text file
184             ( exported from an addressbook -- see above under definitions)
185             or a reference to a scalar containing the file name with the tagged
186             text addressbook. The array must contain at least one element.
187            
188             Parameter 2: Optional: An anonymous array. Each element of the array is either
189             a reference to an array containing the contents of a distribution list (.pml)
190             file or a reference to a scalar containing the distribution list
191             file name.
192            
193             Parameter 3: Optional: A reference to an array. Each element of the array the name
194             of the corresponding distribution list in Parameter 2. This parameter is
195             only necessary if the elements of parameter 2 are array references.
196             When the elements are references to scalers containing
197             the name of the distribution list files, the distribution
198             list name is taken from file name.
199            
200              
201             Returns: a reference to a STF ( see above).
202              
203             =head2 output
204              
205             Input: a reference to a STF ( see above).
206             Returns an array of three items
207              
208             Return 1: A reference to an array containing the main addressbook in tagged
209             text format. This format can be imported into Pegasus.
210            
211             Return 2: A reference to an array. Each element of the array is a
212             a reference an array containing a distrubution list.
213            
214             Return 3: A reference to an array containing the file names of the
215             distribution lists in return 2.
216              
217              
218             =head1 LIMITATIONS
219              
220             This only converts email address, aliases, and mailing lists. Phone numbers,
221             postal addresses and other such data are not converted.
222              
223              
224              
225             =head1 REFERENCES
226              
227             You can find information on Pegasus at http://www.pmail.com/
228            
229              
230             =head1 HISTORY
231              
232             This code is derived from the code used on www.interguru.com/mailconv.htm . The site
233             has been up since 1996 ( but ldif was only included on 1997, when Netscape 3 started
234             using it.) The site gets about 8000 unique visitors a month, many of whom make addressbook
235             conversions. The code has been well tested.
236              
237             =head1 FUTURE DIRECTIONS
238              
239              
240              
241              
242             =head1 BUGS
243              
244             =head1 CHANGES
245              
246             Original Version 2001-Sept-09
247            
248             =head1 COPYRIGHT
249              
250             Copyright (c) 2001 Joe Davidson. All rights reserved.
251             This program is free software; you can redistribute it
252             and/or modify it under the terms of the Perl Artistic License
253             (see http://www.perl.com/perl/misc/Artistic.html). or the
254             GPL copyleft license ( http://www.gnu.org/copyleft/gpl.html)
255              
256              
257             =head1 AUTHOR
258              
259             Mail::Addressbook::Convert was written by Joe Davidson in 2001.
260              
261             =cut
262              
263             #------------------------------------------------------------------------------
264              
265 1     1   1191 use strict;
  1         2  
  1         62  
266              
267              
268             package Mail::Addressbook::Convert::Pegasus;
269              
270 1     1   8 use Mail::Addressbook::Convert::PersistentUtilities;
  1         2  
  1         24  
271 1     1   5 use Mail::Addressbook::Convert::Utilities;
  1         2  
  1         73  
272 1     1   6 use Carp;
  1         2  
  1         60  
273 1     1   6 use File::Basename;
  1         2  
  1         129  
274              
275 1     1   21 use 5.001;
  1         4  
  1         2730  
276             ###############################################################################
277             sub new {
278 1     1 1 688 bless {},shift;
279             }
280              
281             ######################################################################
282              
283              
284             sub scan {
285              
286 1     1 1 15 my $Pegasus = shift;
287              
288 1         2 my $raMainAddressbookArray = shift;
289              
290 1         2 my $raDistributionLists = shift;
291              
292 1         2 my $raDistributionListNames = shift;
293            
294 1         9 my $perUtil = new Mail::Addressbook::Convert::PersistentUtilities();
295              
296 1 50 33     13 unless ( ref($raMainAddressbookArray) =~ /ARRAY/ and @$raMainAddressbookArray > 0 )
297             {
298 0         0 confess "\n You must have an array reference with at least one element as a parameter\n";
299             }
300              
301              
302 1         2 my %aliasOfName;
303 1         3 my $debug = 0;
304              
305 1         2 my (@outputFile, $key, $h, $alias, $oldAlias, $address, $comment);
306 0         0 my ($finalList, $name, $field, $value, $line, $linenumber, $member, @add);
307 0         0 my (@listAlias, $memberNumber, $thisIsDistributionList, $thisIsAnInternetAddress, @distList);
308 0         0 my ($listFileName, %listAliasByListFileName, @listTitle);
309              
310 1         3 my $numAddresses= @$raMainAddressbookArray;
311 1         3 my $numLists= @$raDistributionLists;
312              
313 1         2 my $i;
314 1         3 foreach $i ( @$raMainAddressbookArray)
315             {
316              
317 2         3 @add = @{ getInput($i)};
  2         8  
318 2         14 foreach $linenumber (0..$#add)
319             {
320 72         93 $line= $add[$linenumber];
321 72         80 $line =~ s/\015//g;
322 72         193 $line =~ s/:\s*/:/;
323 72         87 chomp $line;
324              
325 72         148 ($field, $value) = split(/:/,$line);
326 72 100       132 $field = "" unless $field; # to prevent spurious warnings.
327 72 100       308 if ($field =~ /--/)
    100          
    100          
    100          
    100          
328             {
329            
330 6 50       13 if ($key) {$h = $key;}
  6         7  
  0         0  
331             else {$h = $name;}
332 6         9 undef($key);
333 6         8 $alias = $h;
334 6         8 $oldAlias = $alias;
335 6         20 $alias = $perUtil->makeAliasUnique($alias);
336 6         16 $aliasOfName{$name} = $alias;
337 6 50       15 if ($debug) {print "$aliasOfName{$name}, $name\n";}
  0         0  
338 6         18 push (@outputFile,
339             "alias ".$alias." \"".$name.
340             "\"<".$address. ">\n") ;
341 6 50       12 if ($comment)
342             {
343 0         0 push (@outputFile,
344             "note ".$alias. " ".
345             $comment."\n");
346             }
347 6         10 undef($comment);
348              
349             }
350             elsif ($field =~ /Name/)
351             {
352 6         10 $name = $value;
353 6         20 $name =~ s/\s+$//g;
354             }
355             elsif ($field =~ /Key/)
356             {
357 6         11 $key = $value;
358             }
359             elsif ($field =~ /E-mail/)
360             {
361 6         13 $address = $value;
362             }
363             elsif ($field =~ /Notes/)
364             {
365 6         12 $comment = $value;
366             }
367             }
368             }
369 1         6 undef(@add);
370              
371             # START DISTRIBUTION LISTS ########################
372              
373              
374 1 50       8 if (( ref $raDistributionLists) =~ /ARRAY/)
375             {
376             # FIRST LOOP #####################
377 1         2 my $DistFileIndex = 0;
378             #for ($DistFileIndex =1; $DistFileIndex <= $numLists; $DistFileIndex++)
379 1         2 foreach my $distFile1 ( @$raDistributionLists)
380             {
381 2         3 $DistFileIndex++;
382            
383 2         3 my $listFileName;
384 2         3 @add = @{ getInput($distFile1)};
  2         7  
385            
386            
387 2 50       11 if ((ref $distFile1) =~ /SCALAR/)
    0          
388             {
389             # use the name of the distribution list file if it is specified as a file.
390 2         95 $listFileName = basename($$distFile1);
391             # from File::Basename
392            
393             }
394             elsif (ref ($raDistributionListNames) =~ /ARRAY/)
395             {
396 0         0 $listFileName = $raDistributionListNames->[$DistFileIndex];
397             }
398 2 50       6 unless ($listFileName)
399             {
400 0         0 confess "\n Name of distribution list not specified \n";
401             }
402            
403 2         6 foreach $linenumber (0..$#add)
404             {
405 10         15 $line= $add[$linenumber];
406 10         12 $line =~ s/\015//g;
407 10         166 chomp $line;
408 10 100       25 if ($line =~ /TITLE/)
409             {
410 2         7 ($h,$listTitle[$DistFileIndex]) = split(" ",$line,2);
411 2         5 $oldAlias = $listTitle[$DistFileIndex];
412 2         8 $listAlias[$DistFileIndex] = $perUtil->makeAliasUnique($listTitle[$DistFileIndex]);
413            
414            
415 2         3 $listFileName = $listFileName;
416 2         4 $listFileName =~ tr/a-z/A-Z/; # change name to uppercase
417 2         6 $listAliasByListFileName{$listFileName}=
418             $listAlias[$DistFileIndex];
419            
420             }
421 10 100       22 if ($line =~ /^@/)
422             {
423            
424 1         4 $line =~ s/\.PML//;
425            
426             }
427 10 100       30 if (!($line =~ /^\\/))
428             {
429            
430 6         18 $distList[$DistFileIndex] .= $line."::";
431             }
432             }
433 2         9 undef(@add);
434             }
435             ### SECOND LOOP ###########################################
436 1         3 $DistFileIndex =0;
437 1         4 foreach my $distFile2 ( @$raDistributionLists)
438             #for ($DistFileIndex =1; $DistFileIndex <= $numLists; $DistFileIndex++)
439             {
440 2         3 @add = @{ getInput($distFile2)};
  2         8  
441 2         5 $DistFileIndex++;
442 2         7 foreach $memberNumber (0..$#add )
443             {
444 10         12 $member= $add[$memberNumber];
445 10         14 chomp $member;
446 10 50       26 if ($member =~ /([\w\-\+\.\_]+)@([\w\-\+\.]+)/)
447             {
448             # This is probably an Internet address
449 0         0 $finalList .= $member.",";
450 0         0 $thisIsAnInternetAddress = 1;
451             }
452 10 100       20 if ($member =~ /^@/)
453             {
454 1         4 $member =~ s/^@//g;
455 1         3 $thisIsDistributionList = 1;
456 1 50       6 if ($listAliasByListFileName{$member})
457             {
458 1         2 $member = $listAliasByListFileName{$member};
459            
460 1         3 $finalList .= $member.",";
461             }
462            
463             }
464 10 100       27 if($aliasOfName{$member})
465             {
466 5         8 $member = $aliasOfName{$member};
467 5         12 $finalList .= $member.",";
468             }
469            
470             } #end over memberNumber
471 2         5 chop($finalList);
472 2         8 push (@outputFile,
473             "alias ".$listAlias[$DistFileIndex].
474             " ".$finalList."\n") ;
475 2         5 undef($finalList);
476             }
477              
478             } # end if (( ref $raDistributionLists) =~ /ARRAY/)
479 1         11 return \@outputFile;
480             }
481              
482              
483             ########################### sub output #######################
484             sub output
485              
486             {
487 1     1 1 7 my $Pegasus = shift;
488              
489 1         3 my $raInputArray = shift; # reference to input Input data as an array
490              
491 1         3 my ($id,$k, $numberOfGroups) = (3 x 0);
492              
493 1         2 my (%isGroup, @groupalias, @groupaddr, @indivalias,@holdname,$name,$address);
494 0         0 my (%holdname, %aliasid, @indivname, @indivaddr, %indivaddrForLists, %indivnameForLists, %note );
495 0         0 my (@mainAddressBook);
496              
497 1         4 foreach (@$raInputArray )
498             {
499 8         13 chomp;
500 8         28 my @line = split(" ",$_,3);
501 8         12 my $alias= $line[1];
502 8         10 my $aliasold = $alias;
503 8         23 $alias = cleanalias($aliasold);
504 8         11 my $rest = $line[2];
505            
506 8 50       17 if ($line[0] eq "alias") # This is an alias line
507             {
508 8         9 $id++;
509 8         18 $aliasid{$alias} = $id;
510 8         24 my $commas_outside_quotes1 = commas_outside_quotes($rest);
511 8 100       18 if ($commas_outside_quotes1)
512             {
513 2         6 local $^W;
514             # There are commas not enclosed in quotes
515             #we have a group list.
516 2         5 $isGroup{$alias} = 1;
517 2         4 $groupalias[$numberOfGroups] = $alias;
518 2         5 $groupaddr[$numberOfGroups] = $rest;
519 2         42 $groupaddr[$numberOfGroups] =~ s/\s*//g; # get rid of all spaces
520 2         8 $numberOfGroups++;
521             }
522             else # we have an individual address
523             {
524 6         17 local $^W;
525 6         11 $indivalias[$k] = $alias;
526            
527 6 50       20 if ( $rest =~ /
    0          
528             # compound address of form Name
529             {
530 6         18 ($name,$address) = split(/
531 6         11 chop ($address);
532 6         134 $address =~s/>*//g;
533 6         20 $name =~ s/\"//g;
534 6         10 $name =~ s/\'//g;
535 6         8 $indivalias[$k] = $alias;
536 6         12 $holdname{$alias} = $name;
537 6         10 $indivname[$k] = $name;
538 6         10 $rest = $address;
539             }
540             elsif ( $rest =~ /\(/)
541             # compound address of form Address(Name)
542             {
543 0         0 ($address,$name) = split(/\(/,$rest);
544 0         0 $name =~s/\)*|\"|\'//g;
545             #$name =~ s/\"//g;
546             #$name =~ s/\'//g;
547 0         0 chop($name);
548 0         0 chop($address);
549 0         0 $indivalias[$k] = $alias;
550 0         0 $indivname[$k] = $name;
551 0         0 $holdname{$alias} = $name;
552 0         0 $rest = $address;
553             }
554             else # no name given, use the alias as a name
555             {
556 0         0 $indivname[$k] = $alias;
557             }
558 6         7 $indivaddr[$k] = $rest;
559 6         11 $indivaddrForLists{$alias} = $indivaddr[$k] ;
560 6         11 $indivnameForLists{$alias} = $indivname[$k] ;
561 6         21 $k++;
562             }
563             }
564             else #we have a note
565             {
566 0         0 $note{$alias} = $rest;
567             }
568             }
569              
570 1         6 foreach my $kk (0 .. $k-1)
571             {
572 6         10 my $alias1 = $indivalias[$kk];
573              
574              
575              
576 6         13 push @mainAddressBook, "Name: $indivname[$kk]\n";
577 6         11 push @mainAddressBook, "Key: $alias1\n";
578 6         12 push @mainAddressBook, "E-mail address: $indivaddr[$kk]\n";
579             {
580 6         7 local $^W;
  6         13  
581 6         20 push @mainAddressBook, "Notes: $note{$alias1}\n";
582             }
583              
584             }
585              
586             # Write group lists section
587 1         3 my ($localalias, %isLongAlias );
588              
589 1         2 my $raDistributionListArrayReferences = [];
590 1         2 my $raDistributionListNames = [];
591              
592 1         4 foreach my $jj (0 .. $numberOfGroups -1)
593             {
594 2         4 my $alias1 = $groupalias[$jj];
595            
596 2         3 push @{$raDistributionListArrayReferences->[$jj]}, "\\TITLE $alias1 \n";
  2         8  
597            
598 2         3 push @{$raDistributionListArrayReferences->[$jj]}, "\\NOSIG Y\n", "\n";
  2         5  
599            
600 2         6 $raDistributionListNames->[$jj] = $alias1.".pml";
601            
602              
603 2         7 my @groupmembers = split(",",$groupaddr[$jj]);
604 2         5 for (@groupmembers)
605             {
606 6 50       16 if (!/\@/) # We do not have an internet address as a group member
607             {
608 6         17 $localalias = cleanalias($_);
609             }
610             else # We have a internet address -- do not modify
611             {
612 0         0 $localalias = $_;
613             }
614              
615 6 50       17 if ($aliasid{$localalias}) # alias exists
616             {
617              
618 6 100       12 if ($isGroup{$localalias}) #The alias belongs
619             # to a group
620             {
621 1 50       4 if($isLongAlias{$localalias})
622             {
623 0         0 $localalias = substr($localalias,0,8);
624             }
625 1         2 push @{$raDistributionListArrayReferences->[$jj]},"@",$localalias,"\n";
  1         7  
626              
627             }
628             else #An individual alias
629             {
630 5 50       10 if ($indivnameForLists{$localalias} )
631             {
632 5         6 push @{$raDistributionListArrayReferences->[$jj]},
  5         19  
633             $indivnameForLists{$localalias}."\n";
634             }
635             else
636             {
637 0         0 push @{$raDistributionListArrayReferences->[$jj]},
  0         0  
638             $localalias."\n";
639             }
640            
641              
642             }
643             }
644            
645             }
646              
647             }
648 1         13 return \@mainAddressBook, $raDistributionListArrayReferences, $raDistributionListNames ;
649             }
650              
651              
652              
653             ########################### end sub output #######################
654             1;