]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/blob - IkiWiki/Plugin/notifyemail.pm
Add automated test for using the CGI with git, including CVE-2016-10026
[git.ikiwiki.info.git] / IkiWiki / Plugin / notifyemail.pm
1 #!/usr/bin/perl
2 package IkiWiki::Plugin::notifyemail;
4 use warnings;
5 use strict;
6 use IkiWiki 3.00;
8 sub import {
9         hook(type => "formbuilder", id => "notifyemail", call => \&formbuilder);
10         hook(type => "getsetup", id => "notifyemail",  call => \&getsetup);
11         hook(type => "changes", id => "notifyemail", call => \&notify);
12 }
14 sub getsetup () {
15         return
16                 plugin => {
17                         safe => 1,
18                         rebuild => 0,
19                 },
20 }
22 sub formbuilder (@) {
23         my %params=@_;
24         my $form=$params{form};
25         return unless $form->title eq "preferences";
26         my $session=$params{session};
27         my $username=$session->param("name");
28         $form->field(name => "subscriptions", size => 50,
29                 fieldset => "preferences",
30                 comment => "(".htmllink("", "", "ikiwiki/PageSpec", noimageinline => 1).")");
31         if (! $form->submitted) {
32                 $form->field(name => "subscriptions", force => 1,
33                         value => getsubscriptions($username));
34         }
35         elsif ($form->submitted eq "Save Preferences" && $form->validate &&
36                defined $form->field("subscriptions")) {
37                 setsubscriptions($username, scalar $form->field('subscriptions'));
38         }
39 }
41 sub getsubscriptions ($) {
42         my $user=shift;
43         eval q{use IkiWiki::UserInfo};
44         error $@ if $@;
45         IkiWiki::userinfo_get($user, "subscriptions");
46 }
48 sub setsubscriptions ($$) {
49         my $user=shift;
50         my $subscriptions=shift;
51         eval q{use IkiWiki::UserInfo};
52         error $@ if $@;
53         IkiWiki::userinfo_set($user, "subscriptions", $subscriptions);
54 }
56 # Called by other plugins to subscribe the user to a pagespec.
57 sub subscribe ($$) {
58         my $user=shift;
59         my $addpagespec=shift;
60         my $pagespec=getsubscriptions($user);
61         setsubscriptions($user,
62                 length $pagespec ? $pagespec." or ".$addpagespec : $addpagespec);
63 }
65 # Called by other plugins to subscribe an email to a pagespec.
66 sub anonsubscribe ($$) {
67         my $email=shift;
68         my $addpagespec=shift;
69         if (IkiWiki::Plugin::passwordauth->can("anonuser")) {
70                 my $user=IkiWiki::Plugin::passwordauth::anonuser($email);
71                 if (! defined $user) {
72                         error(gettext("Cannot subscribe your email address without logging in."));
73                 }
74                 subscribe($user, $addpagespec);
75         }
76 }
78 sub notify (@) {
79         my @files=@_;
80         return unless @files;
82         eval q{use Mail::Sendmail};
83         error $@ if $@;
84         eval q{use IkiWiki::UserInfo};
85         error $@ if $@;
86         eval q{use URI};
87         error($@) if $@;
89         # Daemonize, in case the mail sending takes a while.
90         defined(my $pid = fork) or error("Can't fork: $!");
91         return if $pid; # parent
92         chdir '/';
93         open STDIN, '/dev/null';
94         open STDOUT, '>/dev/null';
95         POSIX::setsid() or error("Can't start a new session: $!");
96         open STDERR, '>&STDOUT' or error("Can't dup stdout: $!");
98         # Don't need to keep a lock on the wiki as a daemon.
99         IkiWiki::unlockwiki();
101         my $userinfo=IkiWiki::userinfo_retrieve();
102         exit 0 unless defined $userinfo;
104         foreach my $user (keys %$userinfo) {
105                 my $pagespec=$userinfo->{$user}->{"subscriptions"};
106                 next unless defined $pagespec && length $pagespec;
107                 my $email=$userinfo->{$user}->{email};
108                 next unless defined $email && length $email;
110                 foreach my $file (@files) {
111                         my $page=pagename($file);
112                         next unless pagespec_match($page, $pagespec);
113                         my $content="";
114                         my $showcontent=defined pagetype($file);
115                         if ($showcontent) {
116                                 $content=eval { readfile(srcfile($file)) };
117                                 $showcontent=0 if $@;
118                         }
119                         my $url;
120                         if (! IkiWiki::isinternal($page)) {
121                                 $url=urlto($page, undef, 1);
122                         }
123                         elsif (defined $pagestate{$page}{meta}{permalink}) {
124                                 # need to use permalink for an internal page
125                                 $url=URI->new_abs($pagestate{$page}{meta}{permalink}, $config{url});
126                         }
127                         else {
128                                 $url=$config{url}; # crummy fallback url
129                         }
130                         my $pagedesc=$page;
131                         if (defined $pagestate{$page}{meta}{title} &&
132                             length $pagestate{$page}{meta}{title}) {
133                                 $pagedesc=qq{"$pagestate{$page}{meta}{title}"};
134                         }
135                         my $subject=gettext("change notification:")." ".$pagedesc;
136                         if (pagetype($file) eq '_comment') {
137                                 $subject=gettext("comment notification:")." ".$pagedesc;
138                         }
139                         my $prefsurl=IkiWiki::cgiurl_abs(do => 'prefs');
140                         if (IkiWiki::Plugin::passwordauth->can("anonusertoken")) {
141                                 my $token=IkiWiki::Plugin::passwordauth::anonusertoken($userinfo->{$user});
142                                 $prefsurl=IkiWiki::cgiurl_abs(
143                                         do => 'tokenauth',
144                                         name => $user,
145                                         token => $token,
146                                 ) if defined $token;
147                         }
148                         my $template=template("notifyemail.tmpl");
149                         $template->param(
150                                 wikiname => $config{wikiname},
151                                 url => $url,
152                                 prefsurl => $prefsurl,
153                                 showcontent => $showcontent,
154                                 content => $content,
155                         );
156                         sendmail(
157                                 To => $email,
158                                 From => "$config{wikiname} <$config{adminemail}>",
159                                 Subject => $subject,
160                                 Message => $template->output,
161                         );
162                 }
163         }
165         exit 0; # daemon child