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);
127 print qq(\t\t<li class="empty">There are no groups yet.</li>\n);
133 print qq(\t<h2>Contact Group "$group"</h2>\n\t<ul class="results">\n);
137 my $cn = $person->name ();
138 my $cn_esc = uri_escape ($cn);
140 print qq(\t\t<li><a href="$MySelf?action=detail&cn=$cn_esc">$cn</a></li>\n);
148 my $cn = param ('cn');
152 my $person = Person->load ($cn);
155 print qq(\t<div>Entry "$cn" could not be loaded from DB.</div>\n);
159 print qq(\t<h2>Details for $cn</h2>\n);
161 my $cn_esc = uri_escape ($cn);
164 <table class="detail">
173 my $values = $person->get ($field);
174 my $num = scalar (@$values);
175 my $print = defined ($FieldNames{$field}) ? $FieldNames{$field} : $field;
182 print qq(\t\t\t<th rowspan="$num">$print</th>\n);
186 print qq(\t\t\t<th>$print</th>\n);
189 for (my $i = 0; $i < $num; $i++)
191 my $val = $values->[$i];
192 print "\t\t<tr>\n" if ($i);
193 print "\t\t\t<td>$val</td>\n",
199 <div class="detail menu">
200 [<a href="$MySelf?action=edit&cn=$cn_esc">edit</a>]
201 [<a href="$MySelf?action=vcard&cn=$cn_esc">vCard</a>]
208 my $search = param ('search');
211 $search =~ s/[^\s\w]//g;
215 print qq(\t<div class="error">Sorry, the empty search is not allowed.</div>\n);
220 my @patterns = split (m/\s+/, $search);
226 push (@filter, [[lastname => $pattern], [firstname => $pattern]]);
229 my @matches = Person->search (@filter);
233 print qq(\t<div>No entries matched your search.</div>\n);
237 if (scalar (@matches) == 1)
239 my $person = shift (@matches);
240 my $cn = $person->name ();
245 print qq(\t<ul class="result">\n);
249 my $cn = $person->name ();
250 my $cn_esc = uri_escape ($cn);
252 print qq(\t\t<li><a href="$MySelf?action=detail&cn=$cn_esc">$cn</a></li>\n);
261 my $cn = param ('cn');
263 $cn = $opts{'cn'} if (defined ($opts{'cn'}));
277 $contacts->{$_} = [] for (@MultiFields);
281 $person = Person->load ($cn);
285 print qq(\t<div class="error">Unable to load CN "$cn". Sorry.</div>\n);
289 $lastname = $person->lastname ();
290 $firstname = $person->firstname ();
294 $contacts->{$_} = $person->get ($_);
298 $lastname = param ('lastname') if (param ('lastname') and $UserID);
299 $firstname = param ('firstname') if (param ('firstname') and $UserID);
304 my @values = grep { $_ } (param ($field));
305 $contacts->{$field} = [@values] if (@values);
308 $lastname = $opts{'lastname'} if (defined ($opts{'lastname'}));
309 $firstname = $opts{'firstname'} if (defined ($opts{'firstname'}));
313 @{$contacts->{$field}} = @{$opts{$field}} if (defined ($opts{$field}));
318 print "\t\t<h2>Edit contact $cn</h2>\n";
322 print "\t\t<h2>Create new contact</h2>\n";
326 <form action="$MySelf" method="post">
327 <input type="hidden" name="action" value="save" />
328 <input type="hidden" name="cn" value="$cn" />
335 print qq(\t\t\t\t<td><input type="text" name="lastname" value="$lastname" /></td>\n);
339 print qq(\t\t\t\t<td>$lastname</td>\n);
348 print qq(\t\t\t\t<td><input type="text" name="firstname" value="$firstname" /></td>\n);
352 print qq(\t\t\t\t<td>$firstname</td>\n);
355 print "\t\t\t</tr>\n";
360 my $print = defined ($FieldNames{$field}) ? $FieldNames{$field} : $field;
361 my @values = @{$contacts->{$field}};
372 <td><input type="text" name="$field" value="$value" /></td>
382 print qq(\t\t\t\t\t<input type="submit" name="button" value="Update" />\n) if ($UserID);
384 <input type="submit" name="button" value="Save" />
394 my $cn = $UserID ? param ('cn') : $UserCN;
402 die unless ($UserID);
404 if (!param ('lastname') or !param ('firstname'))
406 print qq(\t<div class="error">You have to give both, first and lastname, to identify this record.</div>\n);
407 action_edit (cn => '');
411 my $lastname = param ('lastname');
412 my $firstname = param ('firstname');
418 my @values = grep { $_ } (param ($field));
419 $contacts->{$field} = [@values] if (@values);
422 my $person = Person->create (lastname => $lastname, firstname => $firstname, %$contacts);
426 print qq(\t<div class="error">Unable to save entry. Sorry.</div>\n);
430 $cn = $person->name ();
432 if (param ('button') eq 'Update')
434 action_edit (cn => $cn);
444 my $cn = $UserID ? param ('cn') : $UserCN;
445 my $person = Person->load ($cn);
447 die unless ($person);
451 my $lastname = param ('lastname');
452 my $firstname = param ('firstname');
454 $person->lastname ($lastname) if ($lastname and $lastname ne $person->lastname ());
455 $person->firstname ($firstname) if ($firstname and $firstname ne $person->firstname ());
457 $cn = $person->name ();
464 my @values = grep { $_ } (param ($field));
465 $contacts->{$field} = [@values] if (@values);
472 if (defined ($contacts->{$field}))
474 my $values = $contacts->{$field};
475 $person->set ($field, $values);
479 $person->set ($field, []);
483 if (param ('button') eq 'Update' or !$UserID)
485 action_edit (cn => $cn);
495 my $cn = param ('cn');
499 my $person = Person->load ($cn);
500 die unless ($person);
504 homephone => 'TEL;TYPE=home,voice',
505 cellphone => 'TEL;TYPE=cell',
506 officephone => 'TEL;TYPE=work,voice',
507 fax => 'TEL;TYPE=fax',
513 my $sn = $person->lastname ();
514 my $gn = $person->firstname ();
515 my $cn_esc = uri_escape ($cn);
518 Content-Type: text/x-vcard
519 Content-Disposition: attachment; filename="$cn.vcf"
530 my $vc_fld = $vcard_types{$field};
531 my $values = $person->get ($field);
536 print "$vc_fld:$value\n";
545 $title = q(octo's Lightweight Address Book) unless ($title);
548 Content-Type: text/html; charset=UTF-8
552 <title>$title</title>
553 <style type="text/css">
558 background-color: white;
564 background-color: yellow;
568 border: 2px solid red;
574 background-color: white;
584 border-top: 1px solid black;
602 background-color: #cccccc;
608 background-color: #999999;
621 my $search = param ('search') || '';
624 <form action="$MySelf" method="post">
625 <input type="hidden" name="action" value="browse" />
626 <input type="submit" name="button" value="Browse" />
628 <form action="$MySelf" method="post">
629 <input type="hidden" name="action" value="search" />
630 <input type="text" name="search" value="$search" />
631 <input type="submit" name="button" value="Search" />
633 <form action="$MySelf" method="post">
634 <input type="hidden" name="action" value="edit" />
635 <input type="hidden" name="dn" value="" />
636 <input type="submit" name="button" value="Add New" />
642 print "\t\t<h1>octo's Lightweight Address Book</h1>\n";
648 <div class="foot">octo's Lightweight Address Book <octo at verplant.org></div>
656 my $file = '/var/www/html/cgi.verplant.org/address/book.conf';
659 open ($fh, "< $file") or die ("open ($file): $!");
665 if ($line =~ m/^(\w+):\s*"(.+)"\s*$/)
670 $Config{$key} = $val;
676 for (qw(uri bind_dn password))
678 die ("Not defined: $_") unless (defined ($Config{$_}));