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

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