Removed /usr/local from CDPATH
[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 @fields = qw(userid timestamp sender subject);
378   my ($err, $msg) = $MAPS::db->find(
379     'email',
380     "userid='$userid'",
381     \@fields,
382   );
383
384 my ($timestamp, $sender, $subject);
385
386 format EMAIL =
387 @<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
388 $timestamp,$sender,$subject
389 .
390
391 $~ = "EMAIL";
392   while (my $rec = $MAPS::db->getnext) {
393     last unless $rec->{userid};
394
395    $timestamp = $rec->{timestamp} || '<undef>';
396    $sender    = $rec->{sender}    || '<undef>';
397    $subject   = $rec->{subject}   || '<undef>';
398
399     write();
400   } # while
401 } # ShowEmail
402
403 sub ShowLog($) {
404   my ($how_many) = @_;
405
406   $how_many = defined $how_many ? $how_many : -20;
407
408   my $handle = FindLog($how_many);
409
410   my ($userid, $timestamp, $sender, $type, $message);
411
412 format LOG =
413 @<<<<<<<<<<<<<<<<<<<@<<<<<<<<< @<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
414 $timestamp,$type,$sender,$message
415 .
416 $~ = "LOG";
417
418   my $count = 0;
419
420   while (my $rec = GetLog) {
421     $timestamp = $rec->{timestamp} || '';
422     $type      = $rec->{type}      || '';
423     $sender    = $rec->{sender}    || '';
424     $message   = $rec->{message}   || '';
425
426     $count++;
427
428     last if $count > $how_many;
429
430     write;
431   } # while
432
433   return;
434 } # ShowLog
435
436 sub ShowList($) {
437   my ($type) = @_;
438
439   my $lines = 10;
440   my $next  = 0;
441   my @list;
442   my %record;
443
444 format LIST =
445 @>> @<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<
446 $record{sequence},$record{pattern},$record{domain},$record{comment}
447 .
448 $~ = "LIST";
449
450   # TODO: Why does ReturnList return a one entry array with a many entry array
451   # of hashes. Seems it should just return $list[0], right?
452   while (@list = ReturnList(
453     userid   => $userid,
454     type     => $type,
455     start_at => $next,
456     lines    => $lines)) {
457     for (@{$list[0]}) {
458       %record = %$_;
459
460       # Format blows up if any field is undefined so...
461       $record{pattern} //= '';
462       $record{domain}  //= '';
463       $record{comment} //= '';
464       write();
465     } # for
466
467     print 'Hit any key to continue - q to quit';
468
469     ReadMode 'raw';
470     my $key = ReadKey(0);
471     ReadMode 'normal';
472
473     if ($key eq 'q' or ord $key == 67) {
474       print "\n";
475
476       last;
477     } # if
478
479     print "\r";
480
481     $next += $lines;
482   } # while
483
484   return;
485 } # ShowList
486
487 sub ShowStats($) {
488   my ($nbr_days) = @_;
489
490   $nbr_days ||= 1;
491
492   my %dates = GetStats(
493     userid => $userid,
494     days   => $nbr_days,
495   );
496
497   for my $date (keys(%dates)) {
498     for (keys(%{$dates{$date}})) {
499       say "$date $_:";
500       say "\t$dates{$date}{$_}";
501     } # for
502   } # for
503 } # ShowStats
504
505 sub Deliver($) {
506   my ($filename) = @_;
507
508   my $message;
509
510   if (!open $message, '<', $filename) {
511     say "Unable to open message file $filename";
512     return;
513   } # if
514
515   my $data;
516
517   while ($message) {
518     $data = $data . $_;
519   } # while
520
521   Whitelist "Andrew\@DeFaria.com", $data;
522
523   close $message;
524
525   return;
526 } # Deliver
527
528 sub ExecuteCmd($){
529   my ($line) = @_;
530
531   my ($cmd, $parm1, $parm2, $parm3, $parm4) = split /\s+/, $line;
532
533   given ($cmd) {
534     when (!$_) {
535       return;
536     } # when
537
538     when (/^\s*resequence\s*$/) {
539       Resequence(GetContext(), $parm1);
540     } # when
541
542     when (/^s*encrypt\s*$/) {
543       EncryptPassword($parm1, $userid);
544     } # when
545
546     when (/^\s*encrypt\s*$/) {
547       EncryptPassword($parm1, $userid);
548     } # when
549
550     when (/^\s*decrypt\s*$/) {
551       DecryptPassword($parm1, $userid);
552     } # when
553
554     when (/^\s*deliver\s*$/) {
555       Deliver($parm1);
556     } # when
557
558     when (/^\s*add2whitelist\s*$/) {
559       if ($parm2) {
560         $parm2 .= ' ' . $parm3
561       } # if
562
563       Add2Whitelist(
564         userid    => GetContext,
565         type      => 'white',
566         sender    => $parm1,
567         retention => $parm2,
568       );
569     } # when
570
571     when (/^\s*showusers\s*$/) {
572       ShowUsers;
573     } # when
574
575     when (/^\s*adduser\s*$/) {
576       AddUser(
577         userid   => $parm1,
578         name     => $parm2,
579         email    => $parm3,
580         password => Encrypt($parm4, $userid),
581       );
582     } # when
583
584     when (/^\s*cleanemail\s*$/) {
585       $parm1 = "9999-12-31 23:59:59" unless $parm1;
586
587       say CleanEmail($parm1);
588     } # when
589
590     when (/^\s*cleanlog\s*$/) {
591       $parm1 = "9999-12-31 23:59:59" unless $parm1;
592
593       say CleanLog($parm1);
594     } # when
595
596     when (/^\s*loadlist\s*$/) {
597       LoadListFile($parm1);
598     } # when
599
600     when (/^\s*loademail\s*$/) {
601       LoadEmail($parm1);
602     } # when
603
604     when (/^\s*dumpemail\s*$/) {
605       DumpEmail($parm1);
606     } # when
607
608     when (/^\s*log\s*$/) {
609       Logmsg(
610         userid  => $userid,
611         type    => $parm1,
612         sender  => $parm2,
613         message => $parm3,
614       );
615     } # when
616
617     when (/^\s*switchuser\s*$/) {
618       SwitchUser($parm1);
619     } # when
620
621     when (/^\s*showuser\s*$/) {
622       ShowUser;
623     } # when
624
625     when (/^\s*showemail\s*$/) {
626       ShowEmail;
627     } # when
628
629     when (/^\s*showlog\s*$/) {
630       ShowLog($parm1);
631     } # when
632
633     when (/^\s*showlist\s*$/) {
634       ShowList($parm1);
635     } # when
636
637     when (/^\s*space\s*$/) {
638       ShowSpace;
639     } # when
640
641     when (/^\s*showstats\s*$/) {
642       ShowStats($parm1);
643     } # when
644
645     when (/^\s*setpassword\s*$/) {
646       SetPassword;
647     } # when
648
649     default {
650       say "Unknown command: $_";
651
652       say "Parm1: $parm1" if $parm1;
653       say "Parm2: $parm2" if $parm2;
654       say "Parm3: $parm3" if $parm3;
655       say "Parm4: $parm4" if $parm4;
656     } # default
657   } # given
658
659   return;
660 } # ExecuteCmd
661
662 my $username = Login2MAPS($userid, $ENV{MAPS_PASSWORD});
663
664 if ($ARGV[0]) {
665   ExecuteCmd join ' ', @ARGV;
666   exit;
667 } # if
668
669 # Use CommandLine
670 $CmdLine::cmdline->set_cmds(%cmds);
671 $CmdLine::cmdline->set_eval(\&ExecuteCmd);
672
673 while (my ($line, $result) = $CmdLine::cmdline->get) {
674   next unless $line;
675
676   last if $line =~ /^\s*exit\s*$/i or $line =~ /^\s*quit\s*$/i;
677
678   ExecuteCmd $line;
679 } # while
680
681 exit;