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

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