Minor fixes
[clearscm.git] / maps / bin / mapsutil.pl
1 #!/usr/bin/perl
2 ################################################################################
3 # File:         $RCSfile: mapsutil,v $
4 # Revision:     $Revision: 1.1 $
5 # Description:  This script implements a small command interpreter to exercise
6 #               MAPS functions.
7 # Author:       Andrew@DeFaria.com
8 # Created:      Fri Nov 29 14:17:21  2002
9 # Modified:     $Date: 2013/06/12 14:05:47 $
10 # Language:     perl
11 #
12 # (c) Copyright 2000-2006, Andrew@DeFaria.com, all rights reserved.
13 #
14 ################################################################################
15 use strict;
16 use warnings;
17
18 use 5.026;
19
20 # For use of the given/when (See https://perlmaven.com/switch-case-statement-in-perl5)
21 no warnings 'experimental';
22
23 use FindBin;
24
25 use Term::ReadKey;
26
27 use lib "$FindBin::Bin/../lib";
28 use lib "$FindBin::Bin/../../lib";
29
30 use MAPS;
31 use MAPSLog;
32 use MyDB;
33
34 use CmdLine;
35 use Utils;
36
37 my %cmds = (
38   adduser => {
39     help        => 'Add a user to MAPS',
40     description => 'Usage: adduser <userid> <name> <email> <password>',
41   },
42   add2whitelist => {
43     help        => 'Add sender to whitelist',
44     description => 'Usage: add2whitelist <sender> <retention>',
45   },
46   cleanlog => {
47     help        => 'Cleans out old log entries',
48     description => 'Usage; cleanlog [timestamp]'
49   },
50   log => {
51     help        => 'Logs a message',
52     description => 'Usage: log <message>',
53   },
54   loadlist => {
55     help        => 'Load a list file',
56     description => 'Usage: loadlist <listfile>',
57   },
58   cleanemail => {
59     help        => 'Cleans out old email entries',
60     description => 'Usage: cleanemail [timestamp]',
61   },
62   deliver => {
63     help        => 'Delivers a message',
64     description => 'Usage: deliver <message>',
65   },
66   loademail => {
67     help        => 'Load an mbox file',
68     description => 'Usage: loademail <mbox>',
69   },
70   dumpemail => {
71     help        => 'Dump email from DB to mbox file',
72     description => 'Usage: ',
73   },
74   decrypt => {
75     help        => 'Decrypt a password',
76     description => 'Usage: decrypt <password>',
77   },
78   switchuser => {
79     help        => 'Switch to user',
80     description => 'Usage: switchuser <userid>',
81   },
82   setpassword => {
83     help        => "Set a user's password",
84     description => 'Usage: setpassword',
85   },
86   showuser => {
87     help        => 'Show current user',
88     description => 'Usage: showuser',
89   },
90   showusers => {
91     help        => 'Shows users in the DB',
92     description => 'Usage: showusers',
93   },
94   showemail => {
95     help        => 'Displays email',
96     description => 'Usage: showemail',
97   },
98   showlog => {
99     help        => 'Displays <nbr> log entires',
100     description => 'Usage: showlog <nbr>',
101   },
102   space => {
103     help        => 'Display space usage',
104     description => 'Usage: space',
105   },
106   showlist => {
107     help        => 'Show list by <type>',
108     description => 'Usage: showlist <type>',
109   },
110   encrypt => {
111     help        => 'Encrypt a password',
112     description => 'Usage: encrypt <password>',
113   },
114   resequence => {
115     help        => 'Resequences a <list>',
116     description => 'Usage: resequence <list>',
117   },
118 );
119
120 my $userid = GetContext;
121
122 sub EncryptPassword($$) {
123   my ($password, $userid) = @_;
124
125   my $encrypted_password = Encrypt $password, $userid;
126
127   say "Encrypted password: '$encrypted_password'";
128
129   return;
130 } # EncryptPassword
131
132 sub DecryptPassword($$) {
133   my ($password, $userid) = @_;
134
135   my $decrypted_password = Decrypt($password, $userid);
136
137   say "Decrypted password: $decrypted_password";
138
139   return;
140 } # DecryptPassword
141
142 sub Resequence($$) {
143   my ($userid, $type) = @_;
144
145   ResequenceList(
146     userid => $userid,
147     type   => $type,
148   );
149 } # Resequence
150
151 sub Login2MAPS($;$) {
152   my ($username, $password) = @_;
153
154   if ($username ne '') {
155     $password = GetPassword unless $password;
156   } # if
157
158   while (Login($username, $password) != 0) {
159     say "Login failed!";
160
161     print "Username:";
162
163     $username = <>;
164
165     if ($username eq '') {
166       say "Login aborted!";
167
168       return undef;
169     } # if
170
171     chomp $username;
172
173     $password = GetPassword;
174   } # if
175
176   return $username;
177 } # Login2MAPS
178
179 sub LoadListFile($) {
180   # This function loads a ".list" file. This is to "import" our old ".list"
181   # files. Note it assumes that the ".list" files have specific names.
182   my ($listfilename) = @_;
183
184   my $listtype;
185
186   if ($listfilename eq "white.list") {
187     $listtype = "white";
188   } elsif ($listfilename eq "black.list") {
189     $listtype = "black";
190   } elsif ($listfilename eq "null.list") {
191     $listtype = "null";
192   } else {
193     say "Unknown list file: $listfilename";
194     return;
195   } # if
196
197   my $listfile;
198
199   if (!open $listfile, '<', $listfilename) {
200     say "Unable to open $listfilename";
201     return;
202   } # if
203
204   my $sequence = 0;
205
206   Info(
207     userid  => $userid,
208     message => "Adding $listfilename to $listtype list",
209   );
210
211   while ($listfile) {
212     chomp;
213     next if m/^#/ || m/^$/;
214
215     my ($pattern, $comment) = split /\,/;
216
217     AddList($listtype, $pattern, 0, $comment);
218     $sequence++;
219   } # while
220
221   if ($sequence == 0) {
222     say "No messages found to load";
223   } elsif ($sequence == 1) {
224     say "Loaded 1 message ";
225   } else {
226     say "Loaded $sequence messages";
227   } # if
228
229   say "from $listfilename";
230
231   close $listfile;
232 } # LoadListFile
233
234 sub LoadEmail($) {
235   # This function loads an mbox file.
236   my ($filename) = @_;
237
238   my $file;
239
240   open $file, '<', $filename
241     or die "Unable to open \"$filename\" - $!\n";
242
243   binmode $file;
244
245   my $nbr_msgs;
246
247   while (!eof $file) {
248     my %msgInfo = ReadMsg *$file;
249
250     $nbr_msgs++;
251
252     AddEmail(
253       userid  => $userid,
254       sender  => $msgInfo{sender},
255       subject => $msgInfo{subject},
256       data    => $msgInfo{data},
257     );
258
259     Info(
260       userid  => $userid,
261       message => "Added message from $msgInfo{sender} to email"
262     );
263   } # while
264
265   if ($nbr_msgs == 0) {
266     print "No messages found to load";
267   } elsif ($nbr_msgs == 1) {
268     print "Loaded 1 message";
269   } else {
270     print "Loaded $nbr_msgs messages";
271   } # if
272
273   say " from $filename";
274 } # LoadEmail
275
276 sub DumpEmail($) {
277   # This function unloads email to a mbox file.
278   my ($filename) = @_;
279
280   my $file;
281
282   open $file, '>', $filename or
283     die "Unable to open \"$filename\" - $!\n";
284
285   binmode $file;
286
287   my $i = 0;
288
289   my ($err, $msg) = $MAPS::db->find(
290     'email',
291     "userid = '$userid'",
292     qw(data),
293   );
294
295   croak $msg if $msg;
296
297   while (my $rec = $MAPS::db->getnext) {
298     say $file $rec->{data};
299     $i++;
300   } # while
301
302   say "$i messages dumped to $file";
303
304   close $file;
305 } # DumpEmail
306
307 sub SwitchUser($) {
308   my ($new_user) = @_;
309
310   if ($new_user = Login2MAPS($new_user)) {
311     say "You are now logged in as $new_user";
312   } # if
313 } # SwitchContext
314
315 sub SetPassword() {
316   FindUser(userid => $userid);
317
318   my $rec = GetUser;
319
320   return unless $rec;
321
322   my $password = GetPassword('Enter new password');
323   my $repeat   = GetPassword('Enter new password again');
324
325   if ($password ne $repeat) {
326     say "Passwords don't match!";
327   } else {
328     $rec->{password} = Encrypt($password, $userid);
329
330     UpdateUser(%$rec);
331
332     say "Password updated";
333   } # if
334
335   return;
336 } # SetPassword
337
338 sub ShowSpace() {
339   my $userid = GetContext;
340
341   my $total_space = Space($userid);
342
343   $total_space = $total_space / (1024 * 1024);
344
345   format TOTALSIZE=
346 Total size @###.### Meg
347 $total_space
348 .
349 $~ = "TOTALSIZE";
350
351   write();
352 } # ShowSpace
353
354 sub ShowUser() {
355   say "Current userid is " . GetContext();
356 } # ShowContext
357
358 sub ShowUsers() {
359   FindUser(
360     fields => ['userid', 'name', 'email'],
361   );
362
363   my $rec;
364
365   format USERLIST =
366 User ID: @<<<<<<<<< Name: @<<<<<<<<<<<<<<<<<<< Email: @<<<<<<<<<<<<<<<<<<<<<<<
367 $rec->{userid},$rec->{name},$rec->{email}
368 .
369 $~ = "USERLIST";
370   while ($rec = GetUser) {
371     last unless $rec->{userid};
372     write;
373   } # while
374 } # ShowUsers
375
376 sub ShowEmail() {
377   my ($err, $msg) = $MAPS::db->find(
378     'email',
379     "userid='$userid'",
380     qw(userid timestamp sender subject),
381   );
382
383 my ($timestamp, $sender, $subject);
384
385 format EMAIL =
386 @<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
387 $timestamp,$sender,$subject
388 .
389
390 $~ = "EMAIL";
391   while (my $rec = $MAPS::db->getnext) {
392     last unless $rec->{userid};
393
394    $timestamp = $rec->{timestamp};
395    $sender    = $rec->{sender};
396    $subject   = $rec->{subject};
397
398     write();
399   } # while
400 } # ShowEmail
401
402 sub ShowLog($) {
403   my ($how_many) = @_;
404
405   $how_many = defined $how_many ? $how_many : -20;
406
407   my $handle = FindLog($how_many);
408
409   my ($userid, $timestamp, $sender, $type, $message);
410
411 format LOG =
412 @<<<<<<<<<<<<<<<<<<<@<<<<<<<<< @<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
413 $timestamp,$type,$sender,$message
414 .
415 $~ = "LOG";
416
417   my $count = 0;
418
419   while (my $rec = GetLog) {
420     $timestamp = $rec->{timestamp} || '';
421     $type      = $rec->{type}      || '';
422     $sender    = $rec->{sender}    || '';
423     $message   = $rec->{message}   || '';
424
425     $count++;
426
427     last if $count > $how_many;
428
429     write;
430   } # while
431
432   return;
433 } # ShowLog
434
435 sub ShowList($) {
436   my ($type) = @_;
437
438   my $lines = 10;
439   my $next  = 0;
440   my @list;
441   my %record;
442
443 format LIST =
444 @>> @<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<
445 $record{sequence},$record{pattern},$record{domain},$record{comment}
446 .
447 $~ = "LIST";
448
449   # TODO: Why does ReturnList return a one entry array with a many entry array
450   # of hashes. Seems it should just return $list[0], right?
451   while (@list = ReturnList(
452     userid   => $userid,
453     type     => $type,
454     start_at => $next,
455     lines    => $lines)) {
456     for (@{$list[0]}) {
457       %record = %$_;
458
459       # Format blows up if any field is undefined so...
460       $record{pattern} //= '';
461       $record{domain}  //= '';
462       $record{comment} //= '';
463       write();
464     } # for
465
466     print 'Hit any key to continue - q to quit';
467
468     ReadMode 'raw';
469     my $key = ReadKey(0);
470     ReadMode 'normal';
471
472     if ($key eq 'q' or ord $key == 67) {
473       print "\n";
474
475       last;
476     } # if
477
478     print "\r";
479
480     $next += $lines;
481   } # while
482
483   return;
484 } # ShowList
485
486 sub ShowStats($) {
487   my ($nbr_days) = @_;
488
489   $nbr_days ||= 1;
490
491   my %dates = GetStats(
492     userid => $userid,
493     days   => $nbr_days,
494   );
495
496   for my $date (keys(%dates)) {
497     for (keys(%{$dates{$date}})) {
498       say "$date $_:";
499       say "\t$dates{$date}{$_}";
500     } # for
501   } # for
502 } # ShowStats
503
504 sub Deliver($) {
505   my ($filename) = @_;
506
507   my $message;
508
509   if (!open $message, '<', $filename) {
510     say "Unable to open message file $filename";
511     return;
512   } # if
513
514   my $data;
515
516   while ($message) {
517     $data = $data . $_;
518   } # while
519
520   Whitelist "Andrew\@DeFaria.com", $data;
521
522   close $message;
523
524   return;
525 } # Deliver
526
527 sub ExecuteCmd($){
528   my ($line) = @_;
529
530   my ($cmd, $parm1, $parm2, $parm3, $parm4) = split /\s+/, $line;
531
532   given ($cmd) {
533     when (!$_) {
534       return;
535     } # when
536
537     when (/^\s*resequence\s*$/) {
538       Resequence(GetContext(), $parm1);
539     } # when
540
541     when (/^s*encrypt\s*$/) {
542       EncryptPassword($parm1, $userid);
543     } # when
544
545     when (/^\s*encrypt\s*$/) {
546       EncryptPassword($parm1, $userid);
547     } # when
548
549     when (/^\s*decrypt\s*$/) {
550       DecryptPassword($parm1, $userid);
551     } # when
552
553     when (/^\s*deliver\s*$/) {
554       Deliver($parm1);
555     } # when
556
557     when (/^\s*add2whitelist\s*$/) {
558       if ($parm2) {
559         $parm2 .= ' ' . $parm3
560       } # if
561
562       Add2Whitelist(
563         userid    => GetContext,
564         type      => 'white',
565         sender    => $parm1,
566         retention => $parm2,
567       );
568     } # when
569
570     when (/^\s*showusers\s*$/) {
571       ShowUsers;
572     } # when
573
574     when (/^\s*adduser\s*$/) {
575       AddUser(
576         userid   => $parm1,
577         name     => $parm2,
578         email    => $parm3,
579         password => Encrypt($parm4, $userid),
580       );
581     } # when
582
583     when (/^\s*cleanemail\s*$/) {
584       $parm1 = "9999-12-31 23:59:59" unless $parm1;
585
586       say CleanEmail($parm1);
587     } # when
588
589     when (/^\s*cleanlog\s*$/) {
590       $parm1 = "9999-12-31 23:59:59" unless $parm1;
591
592       say CleanLog($parm1);
593     } # when
594
595     when (/^\s*loadlist\s*$/) {
596       LoadListFile($parm1);
597     } # when
598
599     when (/^\s*loademail\s*$/) {
600       LoadEmail($parm1);
601     } # when
602
603     when (/^\s*dumpemail\s*$/) {
604       DumpEmail($parm1);
605     } # when
606
607     when (/^\s*log\s*$/) {
608       Logmsg(
609         userid  => $userid,
610         type    => $parm1,
611         sender  => $parm2,
612         message => $parm3,
613       );
614     } # when
615
616     when (/^\s*switchuser\s*$/) {
617       SwitchUser($parm1);
618     } # when
619
620     when (/^\s*showuser\s*$/) {
621       ShowUser;
622     } # when
623
624     when (/^\s*showemail\s*$/) {
625       ShowEmail;
626     } # when
627
628     when (/^\s*showlog\s*$/) {
629       ShowLog($parm1);
630     } # when
631
632     when (/^\s*showlist\s*$/) {
633       ShowList($parm1);
634     } # when
635
636     when (/^\s*space\s*$/) {
637       ShowSpace;
638     } # when
639
640     when (/^\s*showstats\s*$/) {
641       ShowStats($parm1);
642     } # when
643
644     when (/^\s*setpassword\s*$/) {
645       SetPassword;
646     } # when
647
648     default {
649       say "Unknown command: $_";
650
651       say "Parm1: $parm1" if $parm1;
652       say "Parm2: $parm2" if $parm2;
653       say "Parm3: $parm3" if $parm3;
654       say "Parm4: $parm4" if $parm4;
655     } # default
656   } # given
657
658   return;
659 } # ExecuteCmd
660
661 my $username = Login2MAPS($userid, $ENV{MAPS_PASSWORD});
662
663 if ($ARGV[0]) {
664   ExecuteCmd join ' ', @ARGV;
665   exit;
666 } # if
667
668 # Use CommandLine
669 $CmdLine::cmdline->set_cmds(%cmds);
670 $CmdLine::cmdline->set_eval(\&ExecuteCmd);
671
672 while (my ($line, $result) = $CmdLine::cmdline->get) {
673   next unless $line;
674
675   last if $line =~ /^\s*exit\s*$/i or $line =~ /^\s*quit\s*$/i;
676
677   ExecuteCmd $line;
678 } # while
679
680 exit;