source: trunk/lib/Vote/DB/Poll.pm @ 173

Last change on this file since 173 was 173, checked in by nanardon, 15 years ago
  • revert last commit
  • Property svn:keywords set to Id Rev
File size: 16.4 KB
Line 
1package Vote::DB::Poll;
2
3# $Id$
4
5use strict;
6use warnings;
7use Vote;
8use Mail::Mailer;
9use Crypt::RSA;
10use Crypt::CBC;
11use XML::Simple;
12use MIME::Base64;
13use base 'Vote::DB::common';
14use Vote::DB::Ballot;
15use Vote::DB::Voting;
16use Vote::DB::Choice;
17
18=head1 NAME
19
20Vote::Model::Vote - Catalyst Model
21
22=head1 DESCRIPTION
23
24Catalyst Model.
25
26=cut
27
28sub new {
29    my ($class, $dbstring, $voteid) = @_;
30   
31    bless {
32        voteid => $voteid,
33        dbstring => $dbstring,
34        db => Vote::DB::common::_newdb($dbstring),
35    }, $class;
36}
37
38sub voteid { $_[0]->{voteid} }
39
40sub param {
41    my ($self, %attr) = @_;
42
43    keys %attr or return;
44    my @online_f = qw(label start end owner password);
45
46    if (grep { exists($attr{$_}) } @online_f) {
47        my $sth = $self->db->prepare_cached(
48            q{update poll set } .
49            join(',', map { qq("$_" = ?) } grep { exists $attr{$_} } @online_f) .
50            q{ where id = ?}
51        );
52        $sth->execute((map { $attr{$_} } grep { exists $attr{$_} } @online_f), $self->voteid)
53            or do {
54            $self->db->rollback;
55            return;
56        };
57    }
58
59    # vote settings in settings table
60    foreach my $var (keys %attr) {
61        grep { $var eq $_ } @online_f and next;
62        $self->set_settings($var, $attr{$var});
63    }
64    1
65}
66
67sub status {
68    my ($self) = @_;
69   
70    my $sth = $self->db->prepare_cached(
71        q{
72        select (start > now() or start is null) as before,
73               "end" < now() as after
74        from poll
75        where id = ?
76        }
77    );
78    $sth->execute($self->voteid);
79    my $res = $sth->fetchrow_hashref;
80    $sth->finish;
81    $res or return;
82    if ($res->{before}) {
83        return 'BEFORE';
84    } elsif ($res->{after}) {
85        return 'AFTER';
86    } else {
87        return 'RUNNING';
88    }
89}
90
91sub info {
92    my ($self) = @_;
93
94    my $sth = $self->db->prepare_cached(
95        q{
96        select *,
97        to_char("start", 'DD/MM/YYYY') as dstart,
98        to_char("start", 'HH24:MI:SS') as hstart,
99        to_char("end", 'DD/MM/YYYY') as dend,
100        to_char("end", 'HH24:MI:SS') as hend
101        from poll where id = ?
102        }
103    );
104
105    $sth->execute($self->voteid);
106    my $res = $sth->fetchrow_hashref;
107    $sth->finish;
108    if ($res) {
109        my $get = $self->db->prepare_cached(
110            q{select var, val from settings where poll = ?}
111        );
112        $get->execute($self->voteid);
113        while (my $set = $get->fetchrow_hashref) {
114            $res->{$set->{var}} = $set->{val};
115        }
116    }
117    $res->{free_choice} ||= 0; # avoiding undef
118    $res
119}
120
121sub set_settings {
122    my ($self, $var, $val) = @_;
123
124    my $upd = $self->db->prepare_cached(
125        q{update settings set val = ? where poll = ? and var = ?}
126    );
127
128    if ($upd->execute($val, $self->voteid, $var) == 0) {
129        my $add = $self->db->prepare_cached(
130            q{insert into settings (poll, var, val) values (?,?,?)}
131        );
132
133        $add->execute($self->voteid, $var, $val);
134    }
135}
136
137sub signing {
138    my ($self) = @_;
139
140    my $sth = $self->db->prepare_cached(
141        q{
142        select *, voting.key as vkey from voting left join signing
143        on signing.key = voting.key
144        where poll = ? order by voting.mail
145        }
146    );
147    $sth->execute($self->voteid);
148    my @people;
149    while (my $res = $sth->fetchrow_hashref) {
150        push(@people, $res);
151    }
152    @people
153}
154
155sub voting {
156    my ($self, $votingkey) = @_;
157
158    my $sth = $self->db->prepare_cached(
159        q{
160        select key from voting where poll = ? and key = ?
161        }
162    );
163
164    $sth->execute($self->voteid, $votingkey);
165    my $res = $sth->fetchrow_hashref;
166    $sth->finish;
167    return $res ? Vote::DB::Voting->new($self->{dbstring}, $votingkey) : undef;
168}
169
170sub voting_from_mail {
171    my ($self, $mail) = @_;
172
173    my $sth = $self->db->prepare_cached(
174        q{
175        select key from voting where poll = ? and mail = ?
176        }
177    );
178
179    $sth->execute($self->voteid, $mail);
180    my $res = $sth->fetchrow_hashref;
181    $sth->finish;
182    return $res ? Vote::DB::Voting->new($self->{dbstring}, $res->{key}) : undef;
183}
184
185sub voting_keys {
186    my ($self) = @_;
187
188    my $sth = $self->db->prepare_cached(
189        q{
190        select key from voting
191        where poll = ? order by voting.mail
192        }
193    );
194    $sth->execute($self->voteid);
195    my @people;
196    while (my $res = $sth->fetchrow_hashref) {
197        push(@people, $res->{key});
198    }
199    @people
200}
201
202sub voting_info {
203    my ($self) = @_;
204
205    my $sth = $self->db->prepare_cached(
206        q{
207        select *, voting.key as vkey from voting left join signing
208        on signing.key = voting.key
209        where voting.key = ?
210        }
211    );
212    $sth->execute($self->voteid);
213
214    my $res = $sth->fetchrow_hashref;
215    $sth->finish;
216    $res
217}
218
219sub choice {
220    my ($self, $chid) = @_;
221   
222    my $sth = $self->db->prepare_cached(
223        q{
224        select key from choice where poll = ? and key = ?
225        }
226    );
227    $sth->execute($self->voteid, $chid);
228    my $res = $sth->fetchrow_hashref;
229    $sth->finish;
230    return $res ? Vote::DB::Choice->new($self->{dbstring}, $chid) : undef;
231}
232
233sub choices {
234    my ($self) = @_;
235
236    my $sth = $self->db->prepare_cached(
237        q{
238        select key from choice where poll = ?
239        order by label
240        }
241    );
242    $sth->execute($self->voteid);
243    my @ch;
244    while (my $res = $sth->fetchrow_hashref) {
245        push(@ch, $res->{key});
246    }
247    @ch
248}
249
250sub add_choice {
251    my ($self, $label) = @_;
252
253    my $sth = $self->db->prepare_cached(
254        q{insert into choice (poll, label) values (?,?)}
255    );
256
257    $sth->execute($self->voteid, $label) or do {
258        $self->db->rollback;
259        return;
260    };
261
262    1
263}
264
265sub delete_choice {
266    my ($self, $chid) = @_;
267
268    my $sth = $self->db->prepare_cached(
269        q{delete from choice where key = ?}
270    );
271
272    $sth->execute($chid);
273}
274
275sub _register_signing {
276    my ($self, $mail, $referal) = @_;
277
278    my $vinfo = $self->voting_info_id($mail) or return;
279
280    my $sth = $self->db->prepare_cached(
281        q{
282        insert into signing (key, referal) values (?,?)
283        }
284    );
285    $sth->execute($vinfo->{key}, $referal) or do {
286        $self->db->rollback;
287        return;
288    };
289
290    1;
291}
292
293sub _register_ballot {
294    my ($self, $choice, $fchoice) = @_;
295
296    my $addb = $self->db->prepare_cached(
297        q{
298        insert into ballot (id, poll, invalid) values (?,?,?)
299        }
300    );
301    my $uid = Vote::DB::common::gen_uid();
302    $addb->execute($uid, $self->voteid, scalar(@{$fchoice || []}) ? undef : 'f') or do {
303        self->db->rollback;
304        return;
305    };
306
307    $self->_register_ballot_items($uid, $choice, $fchoice) or do {
308        self->db->rollback;
309        return;
310    };
311
312    $uid
313}
314
315sub _register_ballot_items {
316    my ($self, $uid, $choice, $fchoice) = @_;
317
318    my $addbc = $self->db->prepare_cached(
319        q{
320        insert into ballot_item (id, value, fromlist) values (?,?,?)
321        }
322    );
323    foreach (@{ $choice || []}) {
324        $addbc->execute($uid, $_, 't') or do {
325            $self->db->rollback;
326            return;
327        };
328    }
329    foreach (@{ $fchoice || []}) {
330        $_ or next;
331        $addbc->execute($uid, $_, 'f') or do {
332            $self->db->rollback;
333            return;
334        };
335    }
336
337    $uid;
338}
339
340sub register_ballot {
341    my ($self, $vid, $choice, $fchoice, $referal) = @_;
342
343    my $uid;
344    for (0..2) { # 3 try
345    # First we register voting has voted
346    $self->_register_signing($vid, $referal) or return; # TODO error ?
347
348    # registring choices
349    $uid = $self->_register_ballot($choice, $fchoice);
350    defined($uid) and last;
351
352    }
353    # everything went fine, saving!
354    $self->db->commit;
355
356   
357    $uid
358}
359
360sub mail_ballot_confirm {
361    my ($self, $vid, $info) = @_;
362    my $voteinfo = $self->info or return;
363    $info->{ballotid} or return;
364    my $mailer = new Mail::Mailer 'smtp', Server => (Vote->config->{smtp} || 'localhost');
365    $ENV{MAILADDRESS} = $vid;
366    $mailer->open({
367        From => $vid, # TODO allow to configure this
368        To => $vid,
369        Subject => 'Confirmation de vote: ' . $voteinfo->{label},
370        Vote::DB::common::mail_header(),
371    });
372    print $mailer <<EOF;
373
374Vous venez de participer au vote:
375
376--------
377$voteinfo->{label}
378--------
379
380Votre bulletin est idéntifié sous le numéro:
381$info->{ballotid}
382
383Les résultats seront disponibles à cet url:
384$info->{url}
385
386Cordialement.
387EOF
388    $mailer->close
389        or warn "couldn't send whole message: $!\n";
390
391}
392
393sub voting_count {
394    my ($self) = @_;
395
396    my $sth = $self->db->prepare_cached(
397        q{
398        select count(*) from voting
399        where poll = ?
400        }
401    );
402    $sth->execute($self->voteid);
403    my $res = $sth->fetchrow_hashref;
404    $sth->finish;
405    $res->{count}
406}
407
408sub signing_count {
409    my ($self) = @_;
410
411    my $sth = $self->db->prepare_cached(
412        q{
413        select count(*) from signing join voting
414        on voting.key = signing.key where poll = ?
415        }
416    );
417
418    $sth->execute($self->voteid);
419    my $res = $sth->fetchrow_hashref;
420    $sth->finish;
421    $res->{count}
422}
423
424sub ballot_count {
425    my ($self) = @_;
426
427    my $sth = $self->db->prepare_cached(
428        q{
429        select count(*) from ballot where poll = ?
430        }
431    );
432
433    $sth->execute($self->voteid);
434    my $res = $sth->fetchrow_hashref;
435    $sth->finish;
436    $res->{count}
437}
438
439sub ballot_count_nonull {
440    my ($self) = @_;
441
442    my $sth = $self->db->prepare_cached(
443        q{
444        select count(*) from ballot where poll = ?
445        and id in (select id from ballot_item) and
446        (invalid = 'false' or invalid is null)
447        }
448    );
449
450    $sth->execute($self->voteid);
451    my $res = $sth->fetchrow_hashref;
452    $sth->finish;
453    $res->{count}
454}
455
456sub voting_info_id {
457    my ($self, $mail) = @_;
458
459    my $sth = $self->db->prepare_cached(
460        q{
461        select * from voting where mail = ? and poll = ?
462        }
463    );
464    $sth->execute($mail, $self->voteid);
465    my $res = $sth->fetchrow_hashref();
466    $sth->finish;
467    $res
468}
469
470sub auth_voting {
471    my ($self, $mail, $password) = @_;
472    my $userinfo = $self->voting_info_id($mail) or return;
473
474    $userinfo->{passwd} or return;
475    if (crypt($password, $userinfo->{passwd} || '') eq $userinfo->{passwd}) {
476        return 1;
477    } else {
478        return 0;
479    }
480}
481
482sub auth_poll {
483    my ($self, $passwd) = @_;
484
485    my $vinfo = $self->info or return;
486
487    $vinfo->{password} or return;
488    $passwd or return;
489    if (crypt($passwd, $vinfo->{password} || '') eq $vinfo->{password}) {
490        return 1;
491    } else {
492        return 0;
493    }
494}
495
496sub voting_has_sign {
497    my ($self, $user) = @_;
498    warn $user;
499    $self->voting_from_mail($user)->has_sign;
500}
501
502# Requete de decompte des voix:
503
504sub results_count {
505    my ($self) = @_;
506
507    my $sth = $self->db->prepare(
508        q{
509        select count(ballot.id), value from
510        (select ballot.id, coalesce(corrected, value) as "value" from ballot left join ballot_item
511        on ballot.id = ballot_item.id where ballot.poll = ? and (invalid = 'false'or invalid is null)
512        group by ballot.id, coalesce(corrected, value)) as ballot
513        group by value
514        order by count desc, value
515        }
516    );
517    $sth->execute($self->voteid);
518    my @results;
519    while (my $res = $sth->fetchrow_hashref) {
520        push(@results, $res);
521    }
522    @results;
523}
524
525sub results_nonull {
526    my ($self) = @_;
527
528    my $sth = $self->db->prepare(
529        q{
530        select count(ballot.id), value from
531        (select ballot.id, coalesce(corrected, value) as "value" from ballot join ballot_item
532        on ballot.id = ballot_item.id where ballot.poll = ? and (invalid = 'false'or invalid is null)
533        group by ballot.id, coalesce(corrected, value)) as ballot
534        group by value
535        order by count desc, value
536        }
537    );
538    $sth->execute($self->voteid);
539    my @results;
540    while (my $res = $sth->fetchrow_hashref) {
541        push(@results, $res);
542    }
543    \@results;
544}
545
546sub ballot {
547    my ($self, $id) = @_;
548
549    my $sth = $self->db->prepare_cached(
550        q{
551        select id from ballot where poll = ? and id = ?
552        }
553    );
554
555    $sth->execute($self->voteid, $id);
556    my $res = $sth->fetchrow_hashref;
557    $sth->finish;
558    return $res ? Vote::DB::Ballot->new($self->{dbstring}, $id) : undef;
559}
560
561sub list_ballot {
562    my ($self) = @_;
563
564    my $sth = $self->db->prepare_cached(
565        q{
566        select id from ballot where poll = ?
567        order by id
568        }
569    );
570    $sth->execute($self->voteid);
571    my @ids;
572    while (my $res = $sth->fetchrow_hashref) {
573        push(@ids, $res->{id});
574    }
575    @ids
576}
577
578sub list_ballot_needvalid {
579    my ($self) = @_;
580
581    my $sth = $self->db->prepare_cached(
582        q{
583        select id from ballot where poll = ?
584        and invalid is null order by id
585        }
586    );
587    $sth->execute($self->voteid);
588    my @ids;
589    while (my $res = $sth->fetchrow_hashref) {
590        push(@ids, $res->{id});
591    }
592    @ids
593}
594
595sub ballot_untrusted_values {
596    my ($self) = @_;
597
598    my $getval = $self->db->prepare_cached(
599        q{
600        select value from ballot join ballot_item
601        on ballot.id = ballot_item.id
602        where poll = ? and fromlist = false and corrected is null
603        group by value order by value
604        }
605    );
606    $getval->execute($self->voteid);
607    my @vals;
608    while (my $res = $getval->fetchrow_hashref) {
609        push(@vals, $res->{value});
610    }
611    @vals
612}
613
614sub ballot_values {
615    my ($self) = @_;
616
617    my $getval = $self->db->prepare_cached(
618        q{
619        select coalesce(corrected, value) as value from ballot join ballot_item
620        on ballot.id = ballot_item.id
621        where poll = ?
622        group by coalesce(corrected, value) order by coalesce(corrected, value)
623        }
624    );
625    $getval->execute($self->voteid);
626    my @vals;
627    while (my $res = $getval->fetchrow_hashref) {
628        push(@vals, $res->{value});
629    }
630    @vals
631}
632
633sub map_value {
634    my ($self, $from, $to) = @_;
635
636    my $sth = $self->db->prepare_cached(
637        q{
638        update ballot_item set corrected = ? where
639        id in (select id from ballot where poll = ?)
640        and (value = ? or corrected = ?)
641        }
642    );
643
644    $sth->execute($to, $self->voteid, $from, $from) or $self->db->rollback;
645    $self->db->commit;
646}
647
648sub addupd_voting {
649    my ($self, $mail, $id) = @_;
650
651    $mail =~ s/\s*$//;
652    $mail =~ s/^\s*//;
653    $mail = lc($mail);
654    $id =~ s/\s*$//;
655    $id =~ s/^\s//;
656    my $upd = $self->db->prepare_cached(
657        q{
658        update voting set label = ? where mail = ? and poll = ?
659        }
660    );
661
662    if ($upd->execute($id || '', $mail, $self->voteid) == 0) {
663        my $add = $self->db->prepare_cached(q{
664            insert into voting (poll, label, mail) values (?,?,?)
665        });
666
667        $add->execute($self->voteid, $id || '', $mail);
668    }
669}
670
671sub voting_from_file {
672    my ($self, $fh, $delete) = @_;
673
674    if ($delete) {
675        my $sth = $self->db->prepare(q{delete from voting where poll = ?});
676        $sth->execute($self->voteid);
677    }
678
679    while (my $line = <$fh>) {
680        chomp($line);
681        my ($mail, $name) = split(';', $line);
682        $mail or do {
683            $self->db->rollback;
684            return;
685        };
686        $self->addupd_voting($self->voteid, $mail, $name || '');
687    }
688    1;
689}
690
691sub mail_passwd_ifnul {
692    my ($self, $mailinfo) = @_;
693
694    my $list_voting = $self->db->prepare_cached(
695        q{select key from voting where poll = ? and passwd is null or passwd = ''}
696    );
697
698    $list_voting->execute($self->voteid);
699    while (my $res = $list_voting->fetchrow_hashref) {
700        $self->mail_voting_passwd($res->{key}, $mailinfo);
701    }
702}
703
704sub mail_voting_passwd {
705    my ($self, $id, $mailinfo) = @_;
706    $self->voting($id)->mail_voting_passwd($mailinfo); 
707}
708
709# crypto part
710
711sub rsa {
712    my ($self) = @_;
713    $self->{rsa} ||= new Crypt::RSA ES => 'PKCS1v15';
714}
715
716sub gen_poll_keys {
717    my ($self) = @_;
718    my ($public, $private) = $self->rsa->keygen (
719        Identity  => 'Epoll Vote ' . $self->voteid,
720        Size      => 768,
721        Password  => undef,
722        Verbosity => 0,
723        KF=>'SSH',
724    ) or die $self->rsa->errstr(); # TODO avoid die
725    $self->param(
726        public_key => $public->serialize,
727        private_key => encode_base64($private->serialize),
728    );
729}
730
731sub public_key {
732    my ($self) = @_;
733    my $serialize = $self->info->{public_key} or return;
734    my $pubkey = Crypt::RSA::Key::Public->new;
735    $pubkey->deserialize($serialize);
736
737    $pubkey
738}
739
740=head1 AUTHOR
741
742Thauvin Olivier
743
744=head1 LICENSE
745
746This library is free software, you can redistribute it and/or modify
747it under the same terms as Perl itself or CeCILL.
748
749=cut
750
7511;
Note: See TracBrowser for help on using the repository browser.