8 use CGI::Carp (qw(fatalsToBrowser));
17 our @MultiFields = (qw(address homephone cellphone officephone fax mail uri group));
22 homephone => 'Home Phone',
23 cellphone => 'Cell Phone',
24 officephone => 'Office Phone',
27 uri => 'URI (Homepage)',
31 our $MySelf = $ENV{'SCRIPT_NAME'};
33 our $Action = param ('action');
34 $Action ||= 'default';
38 browse => [\&html_start, \&action_browse, \&html_end],
39 default => [\&html_start, \&action_browse, \&html_end],
40 detail => [\&html_start, \&action_detail, \&html_end],
41 edit => [\&html_start, \&action_edit, \&html_end],
42 save => [\&html_start, \&action_save, \&html_end],
43 search => [\&html_start, \&action_search, \&html_end],
44 vcard => \&action_vcard
49 # make sure AuthLDAPRemoteUserIsDN is enabled.
50 die unless ($ENV{'REMOTE_USER'});
51 $Config{'base_dn'} = $ENV{'REMOTE_USER'};
55 uri => $Config{'uri'},
56 base_dn => $Config{'base_dn'},
57 bind_dn => $Config{'bind_dn'},
58 password => $Config{'password'}
61 our ($UserCN, $UserID) = Person->get_user ($Config{'base_dn'});
63 if (!$UserID and $Action ne 'save')
73 if (!defined ($Actions{$Action}))
78 if (ref ($Actions{$Action}) eq 'CODE')
80 $Actions{$Action}->();
82 elsif (ref ($Actions{$Action}) eq 'ARRAY')
84 for (@{$Actions{$Action}})
90 #print qq#<div>Authenticated as ($UserCN, $UserID, #, $Config{'base_dn'}, qq#)</div>\n#;
92 Person->disconnect ();
100 my $group = param ('group');
101 $group = shift if (@_);
104 my @all = Person->search ([[group => $group]]);
112 my @g = $person->get ('group');
114 $groups{$_} = 1 for (@g);
117 print qq(\t<h2>Contact Groups</h2>\n\t<ul class="groups">\n);
118 for (sort (keys (%groups)))
121 my $group_esc = uri_escape ($group);
123 print qq(\t\t<li><a href="$MySelf?action=browse&group=$group_esc">$group</a></li>\n);
129 print qq(\t<h2>Contact Group "$group"</h2>\n\t<ul class="results">\n);
133 my $cn = $person->name ();
134 my $cn_esc = uri_escape ($cn);
136 print qq(\t\t<li><a href="$MySelf?action=detail&cn=$cn_esc">$cn</a></li>\n);
144 my $cn = param ('cn');
148 my $person = Person->load ($cn);
151 print qq(\t<div>Entry "$cn" could not be loaded from DB.</div>\n);
155 print qq(\t<h2>Details for $cn</h2>\n);
157 my $cn_esc = uri_escape ($cn);
160 <table class="detail">
169 my $values = $person->get ($field);
170 my $num = scalar (@$values);
171 my $print = defined ($FieldNames{$field}) ? $FieldNames{$field} : $field;
178 print qq(\t\t\t<th rowspan="$num">$print</th>\n);
182 print qq(\t\t\t<th>$print</th>\n);
185 for (my $i = 0; $i < $num; $i++)
187 my $val = $values->[$i];
188 print "\t\t<tr>\n" if ($i);
189 print "\t\t\t<td>$val</td>\n",
195 <div class="detail menu">
196 [<a href="$MySelf?action=edit&cn=$cn_esc">edit</a>]
197 [<a href="$MySelf?action=vcard&cn=$cn_esc">vCard</a>]
204 my $search = param ('search');
207 $search =~ s/[^\s\w]//g;
211 print qq(\t<div class="error">Sorry, the empty search is not allowed.</div>\n);
216 my @patterns = split (m/\s+/, $search);
222 push (@filter, [[lastname => $pattern], [firstname => $pattern]]);
225 my @matches = Person->search (@filter);
229 print qq(\t<div>No entries matched your search.</div>\n);
233 if (scalar (@matches) == 1)
235 my $person = shift (@matches);
236 my $cn = $person->name ();
241 print qq(\t<ul class="result">\n);
245 my $cn = $person->name ();
246 my $cn_esc = uri_escape ($cn);
248 print qq(\t\t<li><a href="$MySelf?action=detail&cn=$cn_esc">$cn</a></li>\n);
257 my $cn = param ('cn');
259 $cn = $opts{'cn'} if (defined ($opts{'cn'}));
273 $contacts->{$_} = [] for (@MultiFields);
277 $person = Person->load ($cn);
281 print qq(\t<div class="error">Unable to load CN "$cn". Sorry.</div>\n);
285 $lastname = $person->lastname ();
286 $firstname = $person->firstname ();
287 $contacts->{'address'} = $person->address ();
288 $contacts->{'homephone'} = $person->homephone ();
289 $contacts->{'cellphone'} = $person->cellphone ();
290 $contacts->{'officephone'} = $person->officephone ();
291 $contacts->{'fax'} = $person->fax ();
292 $contacts->{'mail'} = $person->mail ();
293 $contacts->{'uri'} = $person->uri ();
294 $contacts->{'group'} = $person->group ();
297 $lastname = param ('lastname') if (param ('lastname') and $UserID);
298 $firstname = param ('firstname') if (param ('firstname') and $UserID);
300 get_contacts ($contacts);
302 $lastname = $opts{'lastname'} if (defined ($opts{'lastname'}));
303 $firstname = $opts{'firstname'} if (defined ($opts{'firstname'}));
307 @{$contacts->{$field}} = @{$opts{$field}} if (defined ($opts{$field}));
312 print "\t<h2>Edit contact $cn</h2>\n";
316 print "\t<h2>Create new contact</h2>\n";
321 my $selected = @_ ? shift : '';
323 my @options = ([none => '-- Contact --']);
328 my $print = defined ($FieldNames{$field}) ? $FieldNames{$field} : $field;
329 push (@options, [$field, $print]);
332 print qq(<select name="c_type">\n);
335 my ($field, $print) = @$_;
336 my $sel = $field eq $selected ? ' selected="selected"' : '';
337 print qq(\t\t\t\t<option value="$field"$sel>$print</option>\n);
339 print qq(\t\t\t</select>);
343 <form action="$MySelf" method="post">
344 <input type="hidden" name="action" value="save" />
345 <input type="hidden" name="cn" value="$cn" />
352 print qq(\t\t\t<td><input type="text" name="lastname" value="$lastname" /></td>\n);
356 print qq(\t\t\t<td>$lastname</td>\n);
365 print qq(\t\t\t<td><input type="text" name="firstname" value="$firstname" /></td>\n);
369 print qq(\t\t\t<td>$firstname</td>\n);
377 my @values = @{$contacts->{$field}};
379 @values = ('') unless (@values);
387 print "</td>\n", <<EOF;
388 <td><input type="text" name="c_value" value="$value" /></td>
397 print "</td>\n", <<EOF;
398 <td><input type="text" name="c_value" value="" /></td>
401 <td colspan="2"><input type="submit" name="button" value="Save" /></td>
410 my $cn = $UserID ? param ('cn') : $UserCN;
418 die unless ($UserID);
420 if (!param ('lastname') or !param ('firstname'))
422 print qq(\t<div class="error">You have to give both, first and lastname, to identify this record.</div>\n);
423 action_edit (cn => '');
427 my $lastname = param ('lastname');
428 my $firstname = param ('firstname');
430 my $contacts = get_contacts ();
432 my $person = Person->create (lastname => $lastname, firstname => $firstname, %$contacts);
436 print qq(\t<div class="error">Unable to save entry. Sorry.</div>\n);
440 $cn = $person->name ();
447 my $cn = $UserID ? param ('cn') : $UserCN;
448 my $person = Person->load ($cn);
450 die unless ($person);
454 my $lastname = param ('lastname');
455 my $firstname = param ('firstname');
457 $person->lastname ($lastname) if ($lastname and $lastname ne $person->lastname ());
458 $person->firstname ($firstname) if ($firstname and $firstname ne $person->firstname ());
460 $cn = $person->name ();
463 my $contacts = get_contacts ();
469 if (defined ($contacts->{$field}))
471 my $values = $contacts->{$field};
472 $person->set ($field, $values);
476 $person->set ($field, []);
486 action_edit (cn => $cn);
492 my $cn = param ('cn');
496 my $person = Person->load ($cn);
497 die unless ($person);
501 homephone => 'TEL;TYPE=home,voice',
502 cellphone => 'TEL;TYPE=cell',
503 officephone => 'TEL;TYPE=work,voice',
504 fax => 'TEL;TYPE=fax',
510 my $sn = $person->lastname ();
511 my $gn = $person->firstname ();
512 my $cn_esc = uri_escape ($cn);
515 Content-Type: text/x-vcard
516 Content-Disposition: attachment; filename="$cn.vcf"
527 my $vc_fld = $vcard_types{$field};
528 my $values = $person->get ($field);
533 print "$vc_fld:$value\n";
542 $title = q(octo's Lightweight Address Book) unless ($title);
545 Content-Type: text/html; charset=UTF-8
549 <title>$title</title>
550 <style type="text/css">
555 background-color: white;
561 background-color: yellow;
565 border: 2px solid red;
571 background-color: white;
581 border-top: 1px solid black;
599 background-color: #cccccc;
605 background-color: #999999;
618 my $search = param ('search') || '';
621 <form action="$MySelf" method="post">
622 <input type="hidden" name="action" value="browse" />
623 <input type="submit" name="button" value="Browse" />
625 <form action="$MySelf" method="post">
626 <input type="hidden" name="action" value="search" />
627 <input type="text" name="search" value="$search" />
628 <input type="submit" name="button" value="Search" />
630 <form action="$MySelf" method="post">
631 <input type="hidden" name="action" value="edit" />
632 <input type="hidden" name="dn" value="" />
633 <input type="submit" name="button" value="Add New" />
639 print "\t\t<h1>octo's Lightweight Address Book</h1>\n";
645 <div class="foot">octo's Lightweight Address Book <octo at verplant.org></div>
653 my $file = '/var/www/html/cgi.verplant.org/address/book.conf';
656 open ($fh, "< $file") or die ("open ($file): $!");
662 if ($line =~ m/^(\w+):\s*"(.+)"\s*$/)
667 $Config{$key} = $val;
673 for (qw(uri bind_dn password))
675 die ("Not defined: $_") unless (defined ($Config{$_}));
681 my $contacts = @_ ? shift : {};
683 if (param ('c_value'))
685 my @c_values = param ('c_value');
686 my @c_types = param ('c_type');
690 die if (scalar (@c_values) != scalar (@c_types));
692 for (my $i = 0; $i < scalar (@c_values); $i++)
694 my $type = $c_types[$i];
695 my $value = $c_values[$i];
697 $cts{$type} = [] unless (defined ($cts{$type}));
698 push (@{$cts{$type}}, $value) if ($value);
704 @{$contacts->{$type}} = @{$cts{$type}} if (defined ($cts{$type}));