From 982a174dd0dc4b6f56d3008fa45835dfcdd6869a Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 23 Mar 1998 07:46:02 +0000 Subject: Initial revision --- GPL | 339 +++++++++++++++++++++++++++++++++++ INSTALL | 1 + etc/acp_logfile-parse | 197 ++++++++++++++++++++ etc/example-direct-cardin | 67 +++++++ htdocs/edit/cust_credit.cgi | 97 ++++++++++ htdocs/edit/cust_pay.cgi | 76 ++++++++ htdocs/images/sisd.jpg | Bin 0 -> 22122 bytes htdocs/search/cust_bill.html | 21 +++ htdocs/search/cust_main-payinfo.html | 21 +++ htdocs/search/cust_main.html | 36 ++++ htdocs/search/svc_acct.html | 21 +++ htdocs/search/svc_acct_sm.html | 23 +++ htdocs/search/svc_domain.html | 22 +++ site_perl/table_template-svc.pm | 107 +++++++++++ site_perl/table_template-unique.pm | 66 +++++++ site_perl/table_template.pm | 66 +++++++ 16 files changed, 1160 insertions(+) create mode 100644 GPL create mode 100644 INSTALL create mode 100755 etc/acp_logfile-parse create mode 100755 etc/example-direct-cardin create mode 100755 htdocs/edit/cust_credit.cgi create mode 100755 htdocs/edit/cust_pay.cgi create mode 100755 htdocs/images/sisd.jpg create mode 100755 htdocs/search/cust_bill.html create mode 100755 htdocs/search/cust_main-payinfo.html create mode 100755 htdocs/search/cust_main.html create mode 100755 htdocs/search/svc_acct.html create mode 100755 htdocs/search/svc_acct_sm.html create mode 100755 htdocs/search/svc_domain.html create mode 100644 site_perl/table_template-svc.pm create mode 100644 site_perl/table_template-unique.pm create mode 100644 site_perl/table_template.pm diff --git a/GPL b/GPL new file mode 100644 index 000000000..e77696ae8 --- /dev/null +++ b/GPL @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/INSTALL b/INSTALL new file mode 100644 index 000000000..ff2e43f4e --- /dev/null +++ b/INSTALL @@ -0,0 +1 @@ +See htdocs/docs/index.html diff --git a/etc/acp_logfile-parse b/etc/acp_logfile-parse new file mode 100755 index 000000000..5e258991b --- /dev/null +++ b/etc/acp_logfile-parse @@ -0,0 +1,197 @@ +#!/usr/bin/perl + +### +# WHO WROTE THIS??? +### + +#require "perldb.pl"; + +# Compute SLIP/PPP log times +# Arguments -a Process entire file with totals +# -t Process only totals +# -f File to be processed if not current +# -d processing start date (default is entire file) +# -l to return all totals for dayuse +# -w name of tmp work file for dayuse +# user names + +require "time.pl"; + +$space=' '; + +unless (@ARGV[0]) { + print "Missing Arguments\n"; + print "-a - entire file\n"; + print "-t - totals only\n"; + print "-f - file name to be processed\n"; + print "-d - processing start date (yymmdd)\n"; + print "-l - return totals for dayuse\n"; + print "-w - tmp work file for dayuse\n"; + exit; +} # end if test for missing arguments + +$infile = "/usr/annex/acp_logfile"; +$tmpfile = "/tmp/ppp"; +$n = $#ARGV; +$start_yymmdd = ""; +for ($i = 0; $i <= $n; $i++) { + if ($ARGV[$i] eq "-a") { + $allflag = "true"; + } + elsif ($ARGV[$i] eq "-t") { + $totalflag = "true"; + } + elsif ($ARGV[$i] eq "-f") { + $i++; + $infile = $ARGV[$i]; + } + elsif ($ARGV[$i] eq "-d") { + $i++; + $start_yymmdd = $ARGV[$i]; + } #end start yymmdd + elsif ($ARGV[$i] eq "-l") { + $logflag = "true"; + $totalflag = "true"; + } # end log + elsif ($ARGV[$i] eq "-w") { + $i++; + $tmpfile = $ARGV[$i]; + } # end tmp file + else { + ($arg_user,$arg_yymmdd) = split (/:/, $ARGV[$i]); + $ip_user_date {$arg_user} = $ARGV[$i]; + $userflag = "true"; + } # end else + } # end for 1 = 1 to n + +open (IN,$infile) + || die "Can't open acp_logfile"; + +NEXTUSER: while () { + chop; + ($add,$ether,$port,$date,$time,$type,$action,$user) = split(/:/); + + if ($logflag) { + $start_yymmdd = ''; + if ($ip_user_date{$user}) { + ($ip_user, $start_yymmdd) = + split (/:/, $ip_user_date{$user}); + } # end get date + } # end log flag + if ($start_yymmdd) { + if ($date < $start_yymmdd) { + next NEXTUSER; + } #end date compare + } #end if date + if ($userflag){ + if (!$ip_user_date{$user}) { + next NEXTUSER; + } # end user test + } # end by user or all + if (($totalflag) || + ($allflag) || + ($ip_user_date{$user})) { + if (($type eq 'ppp') || ($type eq 'slip')) { + + if ($action eq 'login') { + $login{$user} = "$time:$date"; + + } + elsif ($action eq 'logout') { + if (!$login{$user}) { + $login{$user} = "010101:$date"; + } #end pad user if carry over + ($stime,$sdate) = split(':',$login{$user}); + $start = &annex2sec($stime); + $end = &annex2sec($time); + + #If we went through midnight, add a day; + if ($end < $start) {$end += 86400;} + $timeon = $end - $start; + + $elapsed{$user} += $timeon; + + if (!$totalflag) { + print (&fmt_user($user), + ' ', &fmt_date($sdate), ' In: ', + &fmt_time($stime),' Out: ', + &fmt_time($time), + ' Elapsed: ', &fmt_sec($timeon), "\n"); + } # end total test + } #end elsif action + } # type = ppp of slip + } # check arguments +} +close IN; + +if ($logflag) { + open (TMPPPP, ">$tmpfile") + || die "Can't open ppp tmp file"; + foreach $user ( sort((keys(%elapsed))) ) { + $log_time = &fmt_sec($elapsed{$user}); + $tmp = join (':', + $user, + $log_time); + print (TMPPPP "$tmp\n"); + } + close (TMPPPP); +} + else { + print "\n\nTotal Time On For Period:\n"; + print "-------------------------\n"; + + foreach $user ( sort((keys(%elapsed))) ) { + print (&fmt_user($user), " ",&fmt_sec($elapsed{$user}), "\n"); + } + } +exit(0); + +#------------------------------------------------------- +#--------------- Subroutines Start Here ---------------- +#------------------------------------------------------- + +sub annex2sec { + local($time) = @_; + return( &time2sec( &break_annex($time) ) ); +} + +sub fmt_date { + local($date) = @_; + + return( substr($date,2,2).'/'.substr($date,4,2).'/'.substr($date,0,2) ); +} + +sub fmt_time { + local($time) = @_; + local($s,$m,$h) = &break_annex($time); + return ("$h:$m:$s"); +} + + +sub break_annex { + local($time) = @_; + local($h,$m,$s); + + $h=substr($time,0,2); + $m=substr($time,2,2); + $s=substr($time,4,2); + + return ($s,$m,$h); +} + +sub fmt_sec { + local(@t) = &sec2time(@_); + @t[2] += (@t[3]*24); + + foreach $a (@t) { + if ($a < 10) {$a = "0$a";} + } + + return ("@t[2]:@t[1]:@t[0]"); +} + +sub fmt_user { + local($user) = @_; + return( $user.substr($space,0,8 - length($user) ).' ' ); +} + diff --git a/etc/example-direct-cardin b/etc/example-direct-cardin new file mode 100755 index 000000000..1a4097221 --- /dev/null +++ b/etc/example-direct-cardin @@ -0,0 +1,67 @@ +#!/usr/local/bin/perl + +### +# THIS IS FROM CYBERCASH (is there a newer version?) +### + +$paymentserverhost = 'localhost'; +$paymentserverport = 8000; +$paymentserversecret = 'two-turntables'; +use CCLib qw(sendmserver); + +# first lets fake up some data +# use time of day and pid to give me my pretend +# order number +# you obviously need to get real data from somewhere... + +$oid = "test$$"; #fake order number. +$amount = 'usd 42.42'; +$ramount = 'usd 24.24'; +$pan = '4111111111111111'; +$name = 'John Q. Doe'; +$addr = '17 Richard Rd.'; +$city = 'Ivyland'; +$state = 'PA'; +$zip = '18974'; +$country = 'USA'; +$exp = '7/97'; + + +%result = &sendmserver('mauthcapture', + 'Order-ID', $oid, + 'Amount', $amount, + 'Card-Number', $pan, + 'Card-Name', $name, + 'Card-Address', $addr, + 'Card-City', $city, + 'Card-State', $state, + 'Card-Zip', $zip, + 'Card-Country', $country, + 'Card-Exp', $exp); + +# +# just dump results to stdout. +# you should process them... +# to allow results to affect operation of your fulfillment... +# +foreach (keys(%result)) { + print " $_ ==> $result{$_}\n"; +} + +print "\n"; + +exit; + +$trans=$result{'MTransactionNumber'}; +$code=$result{'MRetrievalCode'}; + +%result = &sendmserver('return', + 'Order-ID', $oid, + 'Return-Amount',$ramount, + 'Amount',$amount, + ); + +foreach (keys(%result)) { + print " $_ ==> $result{$_}\n"; +} + diff --git a/htdocs/edit/cust_credit.cgi b/htdocs/edit/cust_credit.cgi new file mode 100755 index 000000000..75ef21208 --- /dev/null +++ b/htdocs/edit/cust_credit.cgi @@ -0,0 +1,97 @@ +#!/usr/bin/perl -Tw +# +# cust_credit.cgi: Add a credit (output form) +# +# Usage: cust_credit.cgi custnum [ -paybatch ] +# http://server.name/path/cust_credit?custnum [ -paybatch ] +# +# Note: Should be run setuid root as user nobody. +# +# some hooks in here for modifications as well as additions, but needs (lots) more work. +# also see process/cust_credit.cgi, the script that processes the form. +# +# ivan@voicenet.com 96-dec-05 +# +# paybatch field, differentiates between credits & credits+refunds by commandline +# ivan@voicenet.com 96-dec-08 +# +# added (but commented out) sprintf("%.2f" in amount field. Hmm. +# ivan@voicenet.com 97-jan-3 +# +# paybatch stuff thrown out - has checkbox now instead. +# (well, sort of. still passed around for backward compatability and possible editing hook) +# ivan@voicenet.com 97-apr-21 +# +# rewrite ivan@sisd.com 98-mar-16 + +use strict; +use Date::Format; +use CGI::Base qw(:DEFAULT :CGI); #CGI module +use FS::UID qw(cgisuidsetup getotaker); + +my($cgi) = new CGI::Base; +$cgi->get; +cgisuidsetup($cgi); + +#untaint custnum +$QUERY_STRING =~ /^(\d+)$/; +my($custnum)=$1; + +#untaint otaker +my($otaker)=getotaker; + +SendHeaders(); # one guess. +print < + + Post Credit + + +
+

Post Credit

+
+
+
+END
+
+#crednum
+my($crednum)="";
+print qq!Credit #!, $crednum ? $crednum : " (NEW)", qq!!;
+
+#custnum
+print qq!\nCustomer #$custnum!;
+
+#paybatch
+print qq!!;
+
+#date
+my($date)=time;
+print qq!\nDate: !, time2str("%D",$date), qq!!;
+
+#amount
+my($amount)='';
+print qq!\nAmount \$!;
+
+#refund?
+#print qq! Also post refund!;
+
+#otaker (hidden)
+print qq!!;
+
+#reason
+my($reason)='';
+print qq!\nReason !;
+
+print <
+
+
+END + +print < + + +END + diff --git a/htdocs/edit/cust_pay.cgi b/htdocs/edit/cust_pay.cgi new file mode 100755 index 000000000..a6cb204d1 --- /dev/null +++ b/htdocs/edit/cust_pay.cgi @@ -0,0 +1,76 @@ +#!/usr/bin/perl -Tw +# +# cust_pay.cgi: Add a payment (output form) +# +# Usage: cust_pay.cgi invnum +# http://server.name/path/cust_pay.cgi?invnum +# +# Note: Should be run setuid as user nobody. +# +# some hooks for modifications as well as additions, but needs work. +# +# ivan@voicenet.com 96-dec-11 +# +# rewrite ivan@sisd.com 98-mar-16 + +use strict; +use Date::Format; +use CGI::Base qw(:DEFAULT :CGI); +use FS::UID qw(cgisuidsetup); + +my($cgi) = new CGI::Base; +$cgi->get; +cgisuidsetup($cgi); + +#untaint invnum +$QUERY_STRING =~ /^(\d+)$/; +my($invnum)=$1; + +SendHeaders(); # one guess. +print < + + Enter payment + + +
+

Enter payment

+
+ +
+END
+
+#invnum
+print qq!Invoice #$invnum!;
+
+#date
+my($date)=time;
+print qq!
Date: !, time2str("%D",$date), qq!!; + +#paid +print qq!
Amount \$!; + +#payby +my($payby)="BILL"; +print qq!
Payby: $payby!; + +#payinfo (check # now as payby="BILL" hardcoded.. what to do later?) +my($payinfo)=""; +print qq!
Check #!; + +#paybatch +print qq!!; + +print < +
+
+END + +print < + + +END + diff --git a/htdocs/images/sisd.jpg b/htdocs/images/sisd.jpg new file mode 100755 index 000000000..908a5eaff Binary files /dev/null and b/htdocs/images/sisd.jpg differ diff --git a/htdocs/search/cust_bill.html b/htdocs/search/cust_bill.html new file mode 100755 index 000000000..4adb40e4a --- /dev/null +++ b/htdocs/search/cust_bill.html @@ -0,0 +1,21 @@ + + + Invoice Search + + +
+

Invoice Search

+
+
+ + Search for invoice #: + + +

+ +

+ +
+ + + diff --git a/htdocs/search/cust_main-payinfo.html b/htdocs/search/cust_main-payinfo.html new file mode 100755 index 000000000..92341ad13 --- /dev/null +++ b/htdocs/search/cust_main-payinfo.html @@ -0,0 +1,21 @@ + + + Customer Search + + +
+

Customer Search

+
+
+
+ Search for Credit card #: + + + +

+ +

+
+ + + diff --git a/htdocs/search/cust_main.html b/htdocs/search/cust_main.html new file mode 100755 index 000000000..656943f9c --- /dev/null +++ b/htdocs/search/cust_main.html @@ -0,0 +1,36 @@ + + + Customer Search + + +
+

Customer Search

+
+
+
+ Search for last name: + + using search method(s): + +

Search for company: + + using search methods(s): + +

Note: Fuzzy searching can take a while. Please be patient. + +

+ +
Explanation of search methods: +
    +
  • Fuzzy - Searches for matches that are close to your text. +
  • Exact - Finds exact matches only, but much faster than the other search methods. +
+ + + diff --git a/htdocs/search/svc_acct.html b/htdocs/search/svc_acct.html new file mode 100755 index 000000000..91291be99 --- /dev/null +++ b/htdocs/search/svc_acct.html @@ -0,0 +1,21 @@ + + + Account Search + + +
+

Account Search

+
+
+
+ Search for username: + + +

+ +

+ +
+ + + diff --git a/htdocs/search/svc_acct_sm.html b/htdocs/search/svc_acct_sm.html new file mode 100755 index 000000000..0719856db --- /dev/null +++ b/htdocs/search/svc_acct_sm.html @@ -0,0 +1,23 @@ + + + Mail Alias Search + + +
+

Mail Alias Search

+
+
+
+ Search for mail alias: + (opt.) @ + (req.) + +

+ +

+ +
+ + + + diff --git a/htdocs/search/svc_domain.html b/htdocs/search/svc_domain.html new file mode 100755 index 000000000..533743ba2 --- /dev/null +++ b/htdocs/search/svc_domain.html @@ -0,0 +1,22 @@ + + + Domain Search + + +
+

Domain Search

+
+
+
+ Search for domain: + + +

+ +

+ +
+ + + + diff --git a/site_perl/table_template-svc.pm b/site_perl/table_template-svc.pm new file mode 100644 index 000000000..a8cbaed5e --- /dev/null +++ b/site_perl/table_template-svc.pm @@ -0,0 +1,107 @@ +#!/usr/local/bin/perl -Tw +# +# ivan@voicenet.com 97-jul-21 + +package FS::svc_table; + +use strict; +use Exporter; +use FS::Record qw(fields qsearchs); + +@FS::svc_table::ISA = qw(FS::Record Exporter); + +# Usage: $record = create FS::svc_table ( \%hash ); +# $record = create FS::svc_table ( { field=>value, ... } ); +sub create { + my($proto,$hashref)=@_; + + my($field); + foreach $field (fields('svc_table')) { + $hashref->{$field}='' unless defined $hashref->{$field}; + } + + $proto->new('svc_table',$hashref); + +} + +# Usage: $error = $record -> insert; +sub insert { + my($self)=@_; + my($error); + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + $error=$self->check; + return $error if $error; + + $error = $self->add; + return $error if $error; + + ''; #no error +} + +# Usage: $error = $record -> delete; +sub delete { + my($self)=@_; + my($error); + + $error = $self->del; + return $error if $error; + +} + +# Usage: $error = $newrecord -> replace($oldrecord) +sub replace { + my($new,$old)=@_; + my($error); + + return "(Old) Not a svc_table record!" unless $old->table eq "svc_table"; + return "Can't change svcnum!" + unless $old->getfield('svcnum') eq $new->getfield('svcnum'); + + $error=$new->check; + return $error if $error; + + $error = $new->rep($old); + return $error if $error; + + ''; #no error +} + +# Usage: $error = $record -> suspend; +sub suspend { + ''; #no error (stub) +} + +# Usage: $error = $record -> unsuspend; +sub unsuspend { + ''; #no error (stub) +} + +# Usage: $error = $record -> cancel; +sub cancel { + ''; #no error (stub) +} + +# Usage: $error = $record -> check; +sub check { + my($self)=@_; + return "Not a svc_table record!" unless $self->table eq "svc_table"; + my($recref) = $self->hashref; + + $recref->{svcnum} =~ /^(\d+)$/ or return "Illegal svcnum"; + $recref->{svcnum} = $1; + return "Unknown svcnum" unless + qsearchs('cust_svc',{'svcnum'=> $recref->{svcnum} } ); + + #DATA CHECKS GO HERE! + + ''; #no error +} + +1; + diff --git a/site_perl/table_template-unique.pm b/site_perl/table_template-unique.pm new file mode 100644 index 000000000..32b7e6911 --- /dev/null +++ b/site_perl/table_template-unique.pm @@ -0,0 +1,66 @@ +#!/usr/local/bin/perl -Tw +# +# ivan@voicenet.com 97-jul-1 +# +# added hfields +# ivan@sisd.com 97-nov-13 + +package FS::table_name; + +use strict; +use Exporter; +#use FS::UID qw(getotaker); +use FS::Record qw(fields hfields qsearch qsearchs); + +@FS::table_name::ISA = qw(FS::Record Exporter); +@FS::table_name::EXPORT_OK = qw(hfields); + +# Usage: $record = create FS::table_name ( \%hash ); +# $record = create FS::table_name ( { field=>value, ... } ); +sub create { + my($proto,$hashref)=@_; + + my($field); + foreach $field (fields('table_name')) { + $hashref->{$field}='' unless defined $hashref->{$field}; + } + + $proto->new('table_name',$hashref); +} + +# Usage: $error = $record -> insert; +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +# Usage: $error = $record -> delete; +sub delete { + my($self)=@_; + + $self->del; +} + +# Usage: $error = $newrecord -> replace($oldrecord) +sub replace { + my($new,$old)=@_; + return "(Old) Not a table_name record!" unless $old->table eq "table_name"; + return "Can't change keyfield!" + unless $old->getfield('keyfield') eq $new->getfield('keyfield'); + $new->check or + $new->rep($old); +} + +# Usage: $error = $record -> check; +sub check { + my($self)=@_; + return "Not a table_name record!" unless $self->table eq "table_name"; + my($recref) = $self->hashref; + + ''; #no error +} + +1; + diff --git a/site_perl/table_template.pm b/site_perl/table_template.pm new file mode 100644 index 000000000..cef2d92e8 --- /dev/null +++ b/site_perl/table_template.pm @@ -0,0 +1,66 @@ +#!/usr/local/bin/perl -Tw +# +# ivan@voicenet.com 97-jul-1 +# +# added hfields +# ivan@sisd.com 97-nov-13 + +package FS::table_name; + +use strict; +use Exporter; +#use FS::UID qw(getotaker); +use FS::Record qw(hfields qsearch qsearchs); + +@FS::table_name::ISA = qw(FS::Record Exporter); +@FS::table_name::EXPORT_OK = qw(hfields); + +# Usage: $record = create FS::table_name ( \%hash ); +# $record = create FS::table_name ( { field=>value, ... } ); +sub create { + my($proto,$hashref)=@_; + + my($field); + foreach $field (fields('table_name')) { + $hashref->{$field}='' unless defined $hashref->{$field}; + } + + $proto->new('table_name',$hashref); + +} + +# Usage: $error = $record -> insert; +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +# Usage: $error = $record -> delete; +sub delete { + my($self)=@_; + + $self->del; +} + +# Usage: $error = $newrecord -> replace($oldrecord) +sub replace { + my($new,$old)=@_; + return "(Old) Not a table_name record!" unless $old->table eq "table_name"; + + $new->check or + $new->rep($old); +} + +# Usage: $error = $record -> check; +sub check { + my($self)=@_; + return "Not a table_name record!" unless $self->table eq "table_name"; + my($recref) = $self->hashref; + + ''; #no error +} + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 8ac6789273cc3884d586a790311585776abd9129 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 23 Mar 1998 07:46:04 +0000 Subject: Initial import into CVS --- etc/countries.txt | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 etc/countries.txt diff --git a/etc/countries.txt b/etc/countries.txt new file mode 100644 index 000000000..73c3975ed --- /dev/null +++ b/etc/countries.txt @@ -0,0 +1,239 @@ +AFGHANISTAN AF AFG 004 +ALBANIA AL ALB 008 +ALGERIA DZ DZA 012 +AMERICAN SAMOA AS ASM 016 +ANDORRA AD AND 020 +ANGOLA AO AGO 024 +ANGUILLA AI AIA 660 +ANTARCTICA AQ ATA 010 +ANTIGUA AND BARBUDA AG ATG 028 +ARGENTINA AR ARG 032 +ARMENIA AM ARM 051 +ARUBA AW ABW 533 +AUSTRALIA AU AUS 036 +AUSTRIA AT AUT 040 +AZERBAIJAN AZ AZE 031 +BAHAMAS BS BHS 044 +BAHRAIN BH BHR 048 +BANGLADESH BD BGD 050 +BARBADOS BB BRB 052 +BELARUS BY BLR 112 +BELGIUM BE BEL 056 +BELIZE BZ BLZ 084 +BENIN BJ BEN 204 +BERMUDA BM BMU 060 +BHUTAN BT BTN 064 +BOLIVIA BO BOL 068 +BOSNIA AND HERZEGOWINA BA BIH 070 +BOTSWANA BW BWA 072 +BOUVET ISLAND BV BVT 074 +BRAZIL BR BRA 076 +BRITISH INDIAN OCEAN TERRITORY IO IOT 086 +BRUNEI DARUSSALAM BN BRN 096 +BULGARIA BG BGR 100 +BURKINA FASO BF BFA 854 +BURUNDI BI BDI 108 +CAMBODIA KH KHM 116 +CAMEROON CM CMR 120 +CANADA CA CAN 124 +CAPE VERDE CV CPV 132 +CAYMAN ISLANDS KY CYM 136 +CENTRAL AFRICAN REPUBLIC CF CAF 140 +CHAD TD TCD 148 +CHILE CL CHL 152 +CHINA CN CHN 156 +CHRISTMAS ISLAND CX CXR 162 +COCOS (KEELING) ISLANDS CC CCK 166 +COLOMBIA CO COL 170 +COMOROS KM COM 174 +CONGO CG COG 178 +COOK ISLANDS CK COK 184 +COSTA RICA CR CRI 188 +COTE D'IVOIRE CI CIV 384 +CROATIA (local name: Hrvatska) HR HRV 191 +CUBA CU CUB 192 +CYPRUS CY CYP 196 +CZECH REPUBLIC CZ CZE 203 +DENMARK DK DNK 208 +DJIBOUTI DJ DJI 262 +DOMINICA DM DMA 212 +DOMINICAN REPUBLIC DO DOM 214 +EAST TIMOR TP TMP 626 +ECUADOR EC ECU 218 +EGYPT EG EGY 818 +EL SALVADOR SV SLV 222 +EQUATORIAL GUINEA GQ GNQ 226 +ERITREA ER ERI 232 +ESTONIA EE EST 233 +ETHIOPIA ET ETH 231 +FALKLAND ISLANDS (MALVINAS) FK FLK 238 +FAROE ISLANDS FO FRO 234 +FIJI FJ FJI 242 +FINLAND FI FIN 246 +FRANCE FR FRA 250 +FRANCE, METROPOLITAN FX FXX 249 +FRENCH GUIANA GF GUF 254 +FRENCH POLYNESIA PF PYF 258 +FRENCH SOUTHERN TERRITORIES TF ATF 260 +GABON GA GAB 266 +GAMBIA GM GMB 270 +GEORGIA GE GEO 268 +GERMANY DE DEU 276 +GHANA GH GHA 288 +GIBRALTAR GI GIB 292 +GREECE GR GRC 300 +GREENLAND GL GRL 304 +GRENADA GD GRD 308 +GUADELOUPE GP GLP 312 +GUAM GU GUM 316 +GUATEMALA GT GTM 320 +GUINEA GN GIN 324 +GUINEA-BISSAU GW GNB 624 +GUYANA GY GUY 328 +HAITI HT HTI 332 +HEARD AND MC DONALD ISLANDS HM HMD 334 +HONDURAS HN HND 340 +HONG KONG HK HKG 344 +HUNGARY HU HUN 348 +ICELAND IS ISL 352 +INDIA IN IND 356 +INDONESIA ID IDN 360 +IRAN (ISLAMIC REPUBLIC OF) IR IRN 364 +IRAQ IQ IRQ 368 +IRELAND IE IRL 372 +ISRAEL IL ISR 376 +ITALY IT ITA 380 +JAMAICA JM JAM 388 +JAPAN JP JPN 392 +JORDAN JO JOR 400 +KAZAKHSTAN KZ KAZ 398 +KENYA KE KEN 404 +KIRIBATI KI KIR 296 +KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF KP PRK 408 +KOREA, REPUBLIC OF KR KOR 410 +KUWAIT KW KWT 414 +KYRGYZSTAN KG KGZ 417 +LAO PEOPLE'S DEMOCRATIC REPUBLIC LA LAO 418 +LATVIA LV LVA 428 +LEBANON LB LBN 422 +LESOTHO LS LSO 426 +LIBERIA LR LBR 430 +LIBYAN ARAB JAMAHIRIYA LY LBY 434 +LIECHTENSTEIN LI LIE 438 +LITHUANIA LT LTU 440 +LUXEMBOURG LU LUX 442 +MACAU MO MAC 446 +MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF MK MKD 807 +MADAGASCAR MG MDG 450 +MALAWI MW MWI 454 +MALAYSIA MY MYS 458 +MALDIVES MV MDV 462 +MALI ML MLI 466 +MALTA MT MLT 470 +MARSHALL ISLANDS MH MHL 584 +MARTINIQUE MQ MTQ 474 +MAURITANIA MR MRT 478 +MAURITIUS MU MUS 480 +MAYOTTE YT MYT 175 +MEXICO MX MEX 484 +MICRONESIA, FEDERATED STATES OF FM FSM 583 +MOLDOVA, REPUBLIC OF MD MDA 498 +MONACO MC MCO 492 +MONGOLIA MN MNG 496 +MONTSERRAT MS MSR 500 +MOROCCO MA MAR 504 +MOZAMBIQUE MZ MOZ 508 +MYANMAR MM MMR 104 +NAMIBIA NA NAM 516 +NAURU NR NRU 520 +NEPAL NP NPL 524 +NETHERLANDS NL NLD 528 +NETHERLANDS ANTILLES AN ANT 530 +NEW CALEDONIA NC NCL 540 +NEW ZEALAND NZ NZL 554 +NICARAGUA NI NIC 558 +NIGER NE NER 562 +NIGERIA NG NGA 566 +NIUE NU NIU 570 +NORFOLK ISLAND NF NFK 574 +NORTHERN MARIANA ISLANDS MP MNP 580 +NORWAY NO NOR 578 +OMAN OM OMN 512 +PAKISTAN PK PAK 586 +PALAU PW PLW 585 +PANAMA PA PAN 591 +PAPUA NEW GUINEA PG PNG 598 +PARAGUAY PY PRY 600 +PERU PE PER 604 +PHILIPPINES PH PHL 608 +PITCAIRN PN PCN 612 +POLAND PL POL 616 +PORTUGAL PT PRT 620 +PUERTO RICO PR PRI 630 +QATAR QA QAT 634 +REUNION RE REU 638 +ROMANIA RO ROM 642 +RUSSIAN FEDERATION RU RUS 643 +RWANDA RW RWA 646 +SAINT KITTS AND NEVIS KN KNA 659 +SAINT LUCIA LC LCA 662 +SAINT VINCENT AND THE GRENADINES VC VCT 670 +SAMOA WS WSM 882 +SAN MARINO SM SMR 674 +SAO TOME AND PRINCIPE ST STP 678 +SAUDI ARABIA SA SAU 682 +SENEGAL SN SEN 686 +SEYCHELLES SC SYC 690 +SIERRA LEONE SL SLE 694 +SINGAPORE SG SGP 702 +SLOVAKIA (Slovak Republic) SK SVK 703 +SLOVENIA SI SVN 705 +SOLOMON ISLANDS SB SLB 090 +SOMALIA SO SOM 706 +SOUTH AFRICA ZA ZAF 710 +SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS GS SGS 239 +SPAIN ES ESP 724 +SRI LANKA LK LKA 144 +ST. HELENA SH SHN 654 +ST. PIERRE AND MIQUELON PM SPM 666 +SUDAN SD SDN 736 +SURINAME SR SUR 740 +SVALBARD AND JAN MAYEN ISLANDS SJ SJM 744 +SWAZILAND SZ SWZ 748 +SWEDEN SE SWE 752 +SWITZERLAND CH CHE 756 +SYRIAN ARAB REPUBLIC SY SYR 760 +TAIWAN, PROVINCE OF CHINA TW TWN 158 +TAJIKISTAN TJ TJK 762 +TANZANIA, UNITED REPUBLIC OF TZ TZA 834 +THAILAND TH THA 764 +TOGO TG TGO 768 +TOKELAU TK TKL 772 +TONGA TO TON 776 +TRINIDAD AND TOBAGO TT TTO 780 +TUNISIA TN TUN 788 +TURKEY TR TUR 792 +TURKMENISTAN TM TKM 795 +TURKS AND CAICOS ISLANDS TC TCA 796 +TUVALU TV TUV 798 +UGANDA UG UGA 800 +UKRAINE UA UKR 804 +UNITED ARAB EMIRATES AE ARE 784 +UNITED KINGDOM GB GBR 826 +UNITED STATES US USA 840 +UNITED STATES MINOR OUTLYING ISLANDS UM UMI 581 +URUGUAY UY URY 858 +UZBEKISTAN UZ UZB 860 +VANUATU VU VUT 548 +VATICAN CITY STATE (HOLY SEE) VA VAT 336 +VENEZUELA VE VEN 862 +VIET NAM VN VNM 704 +VIRGIN ISLANDS (BRITISH) VG VGB 092 +VIRGIN ISLANDS (U.S.) VI VIR 850 +WALLIS AND FUTUNA ISLANDS WF WLF 876 +WESTERN SAHARA EH ESH 732 +YEMEN YE YEM 887 +YUGOSLAVIA YU YUG 891 +ZAIRE ZR ZAR 180 +ZAMBIA ZM ZMB 894 +ZIMBABWE ZW ZWE 716 -- cgit v1.2.1 From 0b5b85344c8cb7d94ba895fbd56f483dee756de7 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 23 Mar 1998 08:20:25 +0000 Subject: Initial revision --- fs_passwd/fs_passwdd | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 fs_passwd/fs_passwdd diff --git a/fs_passwd/fs_passwdd b/fs_passwd/fs_passwdd new file mode 100755 index 000000000..582e13ccd --- /dev/null +++ b/fs_passwd/fs_passwdd @@ -0,0 +1,49 @@ +#!/usr/bin/perl -Tw +# +# fs_passwdd +# +# This is run REMOTELY over ssh by fs_passwd_server. +# +# ivan@sisd.com 98-mar-9 + +use strict; +use Socket; + +my($fs_passwdd_socket)="/usr/local/freeside/fs_passwdd_socket"; + +$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin'; +$ENV{'SHELL'} = '/bin/sh'; +$ENV{'IFS'} = " \t\n"; +$ENV{'CDPATH'} = ''; +$ENV{'ENV'} = ''; +$ENV{'BASH_ENV'} = ''; + +$|=1; + +my $uaddr = sockaddr_un($fs_passwdd_socket); +my $proto = getprotobyname('tcp'); + +socket(Server,PF_UNIX,SOCK_STREAM,0) or die "socket: $!"; +unlink($fs_passwdd_socket); +bind(Server, $uaddr) or die "bind: $!"; +listen(Server,SOMAXCONN) or die "listen: $!"; + +my($paddr); +for ( ; $paddr = accept(Client,Server); close Client) { + my($me,$old_password,$new_password,$new_gecos,$new_shell); + + $me=; + $old_password=; + $new_password=; + $new_gecos=; + $new_shell=; + + print $me,$old_password,$new_password,$new_gecos,$new_shell; + my($error); + + $error=; + + print Client $error; + close Client; +} + -- cgit v1.2.1 -- cgit v1.2.1 From 9307a5317a1dcf9fafd8b6bac8ffc70c505f9e2b Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 16 Apr 1998 20:34:09 +0000 Subject: Initial revision --- fs_radlog/fs_radlogd | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100755 fs_radlog/fs_radlogd diff --git a/fs_radlog/fs_radlogd b/fs_radlog/fs_radlogd new file mode 100755 index 000000000..74c2af361 --- /dev/null +++ b/fs_radlog/fs_radlogd @@ -0,0 +1,51 @@ +#!/usr/bin/perl -Tw +# +# ivan@sisd.com 98-mar-23 + +use strict; +use Date::Parse; #but hopefully not + +$|=1; + +my($file,$pos)=@_; +open(FILE,"<$file") or die "Can't open $file: $!"; +seek(FILE,$pos,0) or die "Can't seek: $!"; + +my($datestr); +my(%param); + +$SIG{'HUP'} = sub { print "EOF\n"; exit; }; + +while (1) { + + while () { + next if /^$/; + if ( /^\S/ ) { + chop($datestr=$_); + undef %param; + } else { + warn "Unexpected line: $_"; + } + while () { + if ( /^$/ ) { + #if ( $param{'Acct-Status-Type'} eq 'Stop' ) { + print join("\t", + tell FILE, + %param, + ),"\n"; + #} + last; + } elsif ( /^\s+([\w\-]+)\s\=\s\"?([\w\.\-]+)\"?\s*$/ ) { + $param{$1}=$2; + } else { + warn "Unexpected line: $_"; + } + + } + + } + sleep 1; + seek(FILE,0,1); +} + + -- cgit v1.2.1 -- cgit v1.2.1 From dd013679940cb0a4425eeff4df263e390d9c42e4 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 17 Apr 1998 05:37:07 +0000 Subject: Initial revision --- htdocs/edit/process/cust_credit.cgi | 70 ++++++++++++++ htdocs/edit/process/cust_main.cgi | 102 ++++++++++++++++++++ htdocs/edit/process/cust_pay.cgi | 57 ++++++++++++ htdocs/edit/process/cust_pkg.cgi | 73 +++++++++++++++ htdocs/edit/process/svc_acct.cgi | 87 +++++++++++++++++ htdocs/misc/bill.cgi | 66 +++++++++++++ htdocs/misc/cancel-unaudited.cgi | 85 +++++++++++++++++ htdocs/misc/expire_pkg.cgi | 71 ++++++++++++++ htdocs/misc/susp_pkg.cgi | 68 ++++++++++++++ htdocs/misc/unsusp_pkg.cgi | 68 ++++++++++++++ htdocs/search/cust_bill.cgi | 46 +++++++++ htdocs/view/cust_pkg.cgi | 181 ++++++++++++++++++++++++++++++++++++ htdocs/view/svc_domain.cgi | 76 +++++++++++++++ 13 files changed, 1050 insertions(+) create mode 100755 htdocs/edit/process/cust_credit.cgi create mode 100755 htdocs/edit/process/cust_main.cgi create mode 100755 htdocs/edit/process/cust_pay.cgi create mode 100755 htdocs/edit/process/cust_pkg.cgi create mode 100755 htdocs/edit/process/svc_acct.cgi create mode 100755 htdocs/misc/bill.cgi create mode 100755 htdocs/misc/cancel-unaudited.cgi create mode 100755 htdocs/misc/expire_pkg.cgi create mode 100755 htdocs/misc/susp_pkg.cgi create mode 100755 htdocs/misc/unsusp_pkg.cgi create mode 100755 htdocs/search/cust_bill.cgi create mode 100755 htdocs/view/cust_pkg.cgi create mode 100755 htdocs/view/svc_domain.cgi diff --git a/htdocs/edit/process/cust_credit.cgi b/htdocs/edit/process/cust_credit.cgi new file mode 100755 index 000000000..e660b4c78 --- /dev/null +++ b/htdocs/edit/process/cust_credit.cgi @@ -0,0 +1,70 @@ +#!/usr/bin/perl -Tw +# +# process/cust_credit.cgi: Add a credit (process form) +# +# Usage: post form to: +# http://server.name/path/cust_credit.cgi +# +# Note: Should be run setuid root as user nobody. +# +# ivan@voicenet.com 96-dec-05 -> 96-dec-08 +# +# post a refund if $new_paybatch +# ivan@voicenet.com 96-dec-08 +# +# refunds are no longer applied against a specific payment (paybatch) +# paybatch field removed +# ivan@voicenet.com 97-apr-22 +# +# rewrite ivan@sisd.com 98-mar-16 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use FS::UID qw(cgisuidsetup getotaker); +use FS::cust_credit; + +my($req)=new CGI::Request; # create form object +cgisuidsetup($req->cgi); + +$req->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!"; +my($custnum)=$1; + +$req->param('otaker',getotaker); + +my($new) = create FS::cust_credit ( { + map { + $_, $req->param($_); + } qw(custnum _date amount otaker reason) +} ); + +my($error); +$error=$new->insert; +&idiot($error) if $error; + +#no errors, no refund, so view our credit. +$req->cgi->redirect("../../view/cust_main.cgi?$custnum#history"); + +sub idiot { + my($error)=@_; + CGI::Base::SendHeaders(); # one guess + print < + + Error posting credit/refund + + +
+

Error posting credit/refund

+
+ Your update did not occur because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and press the Post button again. + + +END + +} + diff --git a/htdocs/edit/process/cust_main.cgi b/htdocs/edit/process/cust_main.cgi new file mode 100755 index 000000000..7664dfcb8 --- /dev/null +++ b/htdocs/edit/process/cust_main.cgi @@ -0,0 +1,102 @@ +#!/usr/bin/perl -Tw +# +# process/cust_main.cgi: Edit a customer (process form) +# +# Usage: post form to: +# http://server.name/path/cust_main.cgi +# +# Note: Should be run setuid root as user nobody. +# +# ivan@voicenet.com 96-dec-04 +# +# added referral check +# ivan@voicenet.com 97-jun-4 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-28 +# +# same as above (again) and clean up some stuff ivan@sisd.com 98-feb-23 +# +# Changes to allow page to work at a relative position in server +# Changed 'day' to 'daytime' because Pg6.3 reserves the day word +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::cust_main; + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +#create new record object + +#unmunge agentnum +$req->param('agentnum', + (split(/:/, ($req->param('agentnum'))[0] ))[0] +); + +#unmunge tax +$req->param('tax','') unless defined($req->param('tax')); + +#unmunge refnum +$req->param('refnum', + (split(/:/, ($req->param('refnum'))[0] ))[0] +); + +#unmunge state/county +$req->param('state') =~ /^(\w+)( \((\w+)\))?$/; +$req->param('state', $1); +$req->param('county', $3 || ''); + +my($new) = create FS::cust_main ( { + map { + $_, $req->param("$_") || '' + } qw(custnum agentnum last first ss company address1 address2 city county + state zip country daytime night fax payby payinfo paydate payname tax + otaker refnum) +} ); + +if ( $new->custnum eq '' ) { + + my($error)=$new->insert; + &idiot($error) if $error; + +} else { #create old record object + + my($old) = qsearchs( 'cust_main', { 'custnum', $new->custnum } ); + &idiot("Old record not found!") unless $old; + my($error)=$new->replace($old); + &idiot($error) if $error; + +} + +my($custnum)=$new->custnum; +$req->cgi->redirect("../../view/cust_main.cgi?$custnum#cust_main"); + +sub idiot { + my($error)=@_; + CGI::Base::SendHeaders(); # one guess + print < + + Error updating customer information + + +

+

Error updating customer information

+
+ Your update did not occur because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and submit the form again. + + +END + + exit; + +} + diff --git a/htdocs/edit/process/cust_pay.cgi b/htdocs/edit/process/cust_pay.cgi new file mode 100755 index 000000000..9ec97532b --- /dev/null +++ b/htdocs/edit/process/cust_pay.cgi @@ -0,0 +1,57 @@ +#!/usr/bin/perl -Tw +# +# process/cust_pay.cgi: Add a payment (process form) +# +# Usage: post form to: +# http://server.name/path/cust_pay.cgi +# +# Note: Should be run setuid root as user nobody. +# +# ivan@voicenet.com 96-dec-11 +# +# rewrite ivan@sisd.com 98-mar-16 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use FS::UID qw(cgisuidsetup); +use FS::cust_pay qw(fields); + +my($req)=new CGI::Request; +&cgisuidsetup($req->cgi); + +$req->param('invnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my($invnum)=$1; + +my($new) = create FS::cust_pay ( { + map { + $_, $req->param($_); + } qw(invnum paid _date payby payinfo paybatch) +} ); + +my($error); +$error=$new->insert; + +if ($error) { #error! + CGI::Base::SendHeaders(); # one guess + print < + + Error posting payment + + +

+

Error posting payment

+
+ Your update did not occur because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and press the Post button again. + + +END +} else { #no errors! + $req->cgi->redirect("../../view/cust_bill.cgi?$invnum"); +} + diff --git a/htdocs/edit/process/cust_pkg.cgi b/htdocs/edit/process/cust_pkg.cgi new file mode 100755 index 000000000..6f5bc875a --- /dev/null +++ b/htdocs/edit/process/cust_pkg.cgi @@ -0,0 +1,73 @@ +#!/usr/bin/perl -Tw +# +# process/cust_pkg.cgi: Add/edit packages (process form) +# +# this is for changing packages around, not for editing things within the +# package +# +# Usage: post form to: +# http://server.name/path/cust_pkg.cgi +# +# Note: Should be run setuid root as user nobody. +# +# ivan@voicenet.com 97-mar-21 - 97-mar-24 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-7 - 15 +# +# &cgisuidsetup($cgi) ivan@sisd.com 98-mar-7 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::cust_pkg; + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +#untaint custnum +$req->param('new_custnum') =~ /^(\d+)$/; +my($custnum)=$1; + +my(@remove_pkgnums) = map { + /^(\d+)$/ or die "Illegal remove_pkg value!"; + $1; +} $req->param('remove_pkg'); + +my(@pkgparts); +my($pkgpart); +foreach $pkgpart ( map /^pkg(\d+)$/ ? $1 : (), $req->params ) { + my($num_pkgs)=$req->param("pkg$pkgpart"); + while ( $num_pkgs-- ) { + push @pkgparts,$pkgpart; + } +} + +my($error) = FS::cust_pkg::order($custnum,\@pkgparts,\@remove_pkgnums); + +if ($error) { + CGI::Base::SendHeaders(); + print < + + Error updating packages + + +

+

Error updating packages

+
+ Your update did not occur because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and submit the form again. + + +END +} else { + $req->cgi->redirect("../../view/cust_main.cgi?$custnum#cust_pkg"); +} + diff --git a/htdocs/edit/process/svc_acct.cgi b/htdocs/edit/process/svc_acct.cgi new file mode 100755 index 000000000..8d77ba703 --- /dev/null +++ b/htdocs/edit/process/svc_acct.cgi @@ -0,0 +1,87 @@ +#!/usr/bin/perl -Tw +# +# process/svc_acct.cgi: Add/edit a customer (process form) +# +# Usage: post form to: +# http://server.name/path/svc_acct.cgi +# +# Note: Should br run setuid root as user nobody. +# +# ivan@voicenet.com 96-dec-18 +# +# Changed /u to /u2 +# ivan@voicenet.com 97-may-6 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-17 - 21 +# +# no FS::Search, FS::svc_acct creates FS::cust_svc record, used for adding +# and editing ivan@sisd.com 98-mar-8 +# +# Changes to allow page to work at a relative position in server +# Changed 'password' to '_password' because Pg6.3 reserves the password word +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::svc_acct; + +my($req) = new CGI::Request; # create form object +&cgisuidsetup($req->cgi); + +$req->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my($svcnum)=$1; + +my($old)=qsearchs('svc_acct',{'svcnum'=>$svcnum}) if $svcnum; + +#unmunge popnum +$req->param('popnum', (split(/:/, $req->param('popnum') ))[0] ); + +#unmunge passwd +if ( $req->param('_password') eq '*HIDDEN*' ) { + $req->param('_password',$old->getfield('_password')); +} + +my($new) = create FS::svc_acct ( { + map { + $_, $req->param($_); + } qw(svcnum pkgnum svcpart username _password popnum uid gid finger dir + shell quota slipip) +} ); + +if ( $svcnum ) { + my($error) = $new->replace($old); + &idiot($error) if $error; +} else { + my($error) = $new->insert; + &idiot($error) if $error; + $svcnum = $new->getfield('svcnum'); +} + +#no errors, view account +$req->cgi->redirect("../../view/svc_acct.cgi?" . $svcnum ); + +sub idiot { + my($error)=@_; + CGI::Base::SendHeaders(); # one guess + print < + + Error adding/updating account + + +

+

Error adding/updating account

+
+ Your update did not occur because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and submit the form again. + + +END + exit; +} + diff --git a/htdocs/misc/bill.cgi b/htdocs/misc/bill.cgi new file mode 100755 index 000000000..d41f6d1c9 --- /dev/null +++ b/htdocs/misc/bill.cgi @@ -0,0 +1,66 @@ +#!/usr/bin/perl -Tw +# +# s/FS:Search/FS::Record/ and cgisuidsetup($cgi) ivan@sisd.com 98-mar-13 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::Bill; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +#untaint custnum +$QUERY_STRING =~ /^(\d*)$/; +my($custnum)=$1; +my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum}); +die "Can't find customer!\n" unless $cust_main; + +# ? +bless($cust_main,"FS::Bill"); + +my($error); + +$error = $cust_main->bill( +# 'time'=>$time + ); +&idiot($error) if $error; + +$error = $cust_main->collect( +# 'invoice-time'=>$time, +# 'batch_card'=> 'yes', + 'batch_card'=> 'no', + 'report_badcard'=> 'yes', + ); +&idiot($error) if $error; + +$cgi->redirect("../view/cust_main.cgi?$custnum#history"); + +sub idiot { + my($error)=@_; + CGI::Base::SendHeaders(); # one guess + print < + + Error billing customer + + +

+

Error billing customer

+
+ Your update did not occur because of the following error: +

$error + + +END + + exit; + +} + diff --git a/htdocs/misc/cancel-unaudited.cgi b/htdocs/misc/cancel-unaudited.cgi new file mode 100755 index 000000000..929274f38 --- /dev/null +++ b/htdocs/misc/cancel-unaudited.cgi @@ -0,0 +1,85 @@ +#!/usr/bin/perl -Tw +# +# cancel-unaudited.cgi: Cancel an unaudited account +# +# Usage: cancel-unaudited.cgi svcnum +# http://server.name/path/cancel-unaudited.cgi pkgnum +# +# Note: Should be run setuid freeside as user nobody +# +# ivan@voicenet.com 97-apr-23 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-21 +# +# Search->Record, cgisuidsetup($cgi) ivan@sids.com 98-mar-19 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::cust_svc; +use FS::svc_acct; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +#untaint svcnum +$QUERY_STRING =~ /^(\d+)$/; +my($svcnum)=$1; + +my($svc_acct) = qsearchs('svc_acct',{'svcnum'=>$svcnum}); +&idiot("Unknown svcnum!") unless $svc_acct; + +my($cust_svc) = qsearchs('cust_svc',{'svcnum'=>$svcnum}); +&idiot(qq!This account has already been audited. Cancel the + package instead.!) + if $cust_svc->getfield('pkgnum') ne ''; + +local $SIG{HUP} = 'IGNORE'; +local $SIG{INT} = 'IGNORE'; +local $SIG{QUIT} = 'IGNORE'; +local $SIG{TERM} = 'IGNORE'; +local $SIG{TSTP} = 'IGNORE'; + +my($error); + +bless($svc_acct,"FS::svc_acct"); +$error = $svc_acct->cancel; +&idiot($error) if $error; +$error = $svc_acct->delete; +&idiot($error) if $error; + +bless($cust_svc,"FS::cust_svc"); +$error = $cust_svc->delete; +&idiot($error) if $error; + +$cgi->redirect("../"); + +sub idiot { + my($error)=@_; + SendHeaders(); + print < + + Error cancelling account + + +

+

Error cancelling account

+
+
+ There has been an error cancelling this acocunt: $error + + + +END + exit; +} + diff --git a/htdocs/misc/expire_pkg.cgi b/htdocs/misc/expire_pkg.cgi new file mode 100755 index 000000000..163516627 --- /dev/null +++ b/htdocs/misc/expire_pkg.cgi @@ -0,0 +1,71 @@ +#!/usr/bin/perl -Tw +# +# expire_pkg.cgi: Expire a package +# +# Usage: post form to: +# http://server.name/path/expire_pkg.cgi +# +# Note: Should be run setuid freeside as user nobody +# +# based on susp_pkg +# ivan@voicenet.com 97-jul-29 +# +# ivan@sisd.com 98-mar-17 FS::Search->FS::Record +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use Date::Parse; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::cust_pkg; + +my($req) = new CGI::Request; +&cgisuidsetup($req->cgi); + +#untaint date & pkgnum + +my($date); +if ( $req->param('date') ) { + str2time($req->param('date')) =~ /^(\d+)$/ or die "Illegal date"; + $date=$1; +} else { + $date=''; +} + +$req->param('pkgnum') =~ /^(\d+)$/ or die "Illegal pkgnum"; +my($pkgnum)=$1; + +my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); +my(%hash)=$cust_pkg->hash; +$hash{expire}=$date; +my($new)=create FS::cust_pkg ( \%hash ); +my($error) = $new->replace($cust_pkg); +&idiot($error) if $error; + +$req->cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum')); + +sub idiot { + my($error)=@_; + SendHeaders(); + print < + + Error expiring package + + +
+

Error expiring package

+
+
+ There has been an error expiring this package: $error + + + +END + exit; +} + diff --git a/htdocs/misc/susp_pkg.cgi b/htdocs/misc/susp_pkg.cgi new file mode 100755 index 000000000..7b23caeb2 --- /dev/null +++ b/htdocs/misc/susp_pkg.cgi @@ -0,0 +1,68 @@ +#!/usr/bin/perl -Tw +# +# susp_pkg.cgi: Suspend a package +# +# Usage: susp_pkg.cgi pkgnum +# http://server.name/path/susp_pkg.cgi pkgnum +# +# Note: Should be run setuid freeside as user nobody +# +# probably should generalize this to do cancels, suspensions, unsuspensions, etc. +# +# ivan@voicenet.com 97-feb-27 +# +# now redirects to enter comments +# ivan@voicenet.com 97-may-8 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-21 +# +# FS::Search -> FS::Record ivan@sisd.com 98-mar-17 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::cust_pkg; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +#untaint pkgnum +$QUERY_STRING =~ /^(\d+)$/ || die "Illegal pkgnum"; +my($pkgnum)=$1; + +my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + +bless($cust_pkg,'FS::cust_pkg'); +my($error)=$cust_pkg->suspend; +&idiot($error) if $error; + +$cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum')); + +sub idiot { + my($error)=@_; + SendHeaders(); + print < + + Error suspending package + + +
+

Error suspending package

+
+
+ There has been an error suspending this package: $error + + + +END + exit; +} + diff --git a/htdocs/misc/unsusp_pkg.cgi b/htdocs/misc/unsusp_pkg.cgi new file mode 100755 index 000000000..2f340c6fa --- /dev/null +++ b/htdocs/misc/unsusp_pkg.cgi @@ -0,0 +1,68 @@ +#!/usr/bin/perl -Tw +# +# susp_pkg.cgi: Unsuspend a package +# +# Usage: susp_pkg.cgi pkgnum +# http://server.name/path/susp_pkg.cgi pkgnum +# +# Note: Should be run setuid freeside as user nobody +# +# probably should generalize this to do cancels, suspensions, unsuspensions, etc. +# +# ivan@voicenet.com 97-feb-27 +# +# now redirects to enter comments +# ivan@voicenet.com 97-may-8 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-21 +# +# FS::Search -> FS::Record ivan@sisd.com 98-mar-17 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::cust_pkg; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +#untaint pkgnum +$QUERY_STRING =~ /^(\d+)$/ || die "Illegal pkgnum"; +my($pkgnum)=$1; + +my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + +bless($cust_pkg,'FS::cust_pkg'); +my($error)=$cust_pkg->unsuspend; +&idiot($error) if $error; + +$cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum')); + +sub idiot { + my($error)=@_; + SendHeaders(); + print < + + Error unsuspending package + + +
+

Error unsuspending package

+
+
+ There has been an error unsuspending this package: $error + + + +END + exit; +} + diff --git a/htdocs/search/cust_bill.cgi b/htdocs/search/cust_bill.cgi new file mode 100755 index 000000000..5be84b79e --- /dev/null +++ b/htdocs/search/cust_bill.cgi @@ -0,0 +1,46 @@ +#!/usr/bin/perl -Tw +# +# cust_bill.cgi: Search for invoices (process form) +# +# Usage: post form to: +# http://server.name/path/cust_bill.cgi +# +# Note: Should be run setuid freeside as user nobody. +# +# ivan@voicenet.com 97-apr-4 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); + +my($req)=new CGI::Request; +cgisuidsetup($req->cgi); + +$req->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/; +my($invnum)=$2; + +if ( qsearchs('cust_bill',{'invnum'=>$invnum}) ) { + $req->cgi->redirect("../view/cust_bill.cgi?$invnum"); #redirect +} else { #error + CGI::Base::SendHeaders(); # one guess + print < + + Invoice Search Error + + +
+

Invoice Search Error

+
+ Invoice not found. +
+ + +END + +} + diff --git a/htdocs/view/cust_pkg.cgi b/htdocs/view/cust_pkg.cgi new file mode 100755 index 000000000..04e38326a --- /dev/null +++ b/htdocs/view/cust_pkg.cgi @@ -0,0 +1,181 @@ +#!/usr/bin/perl -Tw +# +# cust_pkg.cgi: View a package +# +# Usage: cust_pkg.cgi pkgnum +# http://server.name/path/cust_pkg.cgi?pkgnum +# +# Note: Should be run setuid freeside as user nobody. +# +# ivan@voicenet.com 96-dec-15 +# +# services section needs to be cleaned up, needs to display extraneous +# entries in cust_pkg! +# ivan@voicenet.com 96-dec-31 +# +# added navigation bar +# ivan@voicenet.com 97-jan-30 +# +# changed and fixed up suspension and cancel stuff, now you can't add +# services to a cancelled package +# ivan@voicenet.com 97-feb-27 +# +# rewrote for new API, still needs to be cleaned up! +# ivan@voicenet.com 97-jul-29 +# +# no FS::Search ivan@sisd.com 98-mar-7 + +use strict; +use Date::Format; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +my(%uiview,%uiadd); +my($part_svc); +foreach $part_svc ( qsearch('part_svc',{}) ) { + $uiview{$part_svc->svcpart}="../view/". $part_svc->svcdb . ".cgi"; + $uiadd{$part_svc->svcpart}="../edit/". $part_svc->svcdb . ".cgi"; +} + +SendHeaders(); # one guess. +print < + + Package View + + +
+

Package View

+
+ +END + +#untaint pkgnum +$QUERY_STRING =~ /^(\d+)$/; +my($pkgnum)=$1; + +#get package record +my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); +die "No package!" unless $cust_pkg; +my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->getfield('pkgpart')}); + +#nav bar +my($custnum)=$cust_pkg->getfield('custnum'); +print qq!
View this customer!, + qq! (#$custnum) | Main menu

!; + +#print info +my($susp,$cancel,$expire)=( + $cust_pkg->getfield('susp'), + $cust_pkg->getfield('cancel'), + $cust_pkg->getfield('expire'), +); +print "
Package #$pkgnum"; +print qq!
Package Information!; +print qq! | Service Information! unless $cancel; +print qq!

\n!; + +my($pkg,$comment)=($part_pkg->getfield('pkg'),$part_pkg->getfield('comment')); +print qq!
Package Information!, + qq!!; +print qq!
Edit this information
!; +print "

Package: $pkg - $comment"; + +my($setup,$bill)=($cust_pkg->getfield('setup'),$cust_pkg->getfield('bill')); +print "
Setup: ", $setup ? time2str("%D",$setup) : "(Not setup)" ,""; +print "
Next bill: ", $bill ? time2str("%D",$bill) : "" ,""; + +if ($susp) { + print "
Suspended: ", time2str("%D",$susp), ""; + print qq! Unsuspend! unless $cancel; +} else { + print qq!
Suspend! unless $cancel; +} + +if ($expire) { + print "
Expire: ", time2str("%D",$expire), ""; +} + print < + +Expire (date): + +END + +if ($cancel) { + print "
Cancelled: ", time2str("%D",$cancel), ""; +} else { + print qq!
Cancel now!; +} + +#otaker +my($otaker)=$cust_pkg->getfield('otaker'); +print "

Order taken by $otaker"; + +unless ($cancel) { + + #services + print <

Service Information +
Click on service to view/edit/add service.

+
Do NOT pick the "Link to existing" option unless you are auditing!!!
+
+ +END + + #list of services this pkgpart includes + my($pkg_svc,%pkg_svc); + foreach $pkg_svc ( qsearch('pkg_svc',{'pkgpart'=> $cust_pkg->pkgpart }) ) { + $pkg_svc{$pkg_svc->svcpart} = $pkg_svc->quantity if $pkg_svc->quantity; + } + + #list of records from cust_svc + my($svcpart); + foreach $svcpart (sort {$a <=> $b} keys %pkg_svc) { + + my($svc)=qsearchs('part_svc',{'svcpart'=>$svcpart})->getfield('svc'); + + my(@cust_svc)=qsearch('cust_svc',{'pkgnum'=>$pkgnum, + 'svcpart'=>$svcpart, + }); + + my($enum); + for $enum ( 1 .. $pkg_svc{$svcpart} ) { + + my($cust_svc); + if ( $cust_svc=shift @cust_svc ) { + my($svcnum)=$cust_svc->svcnum; + print < +END + } else { + print < + + +END + } + + } + warn "WARNING: Leftover services pkgnum $pkgnum!" if @cust_svc;; + } + + print "
Service(View) $svc
+ (Add) $svc + or + (Link to existing) $svc +
"; + +} + +#formatting +print < + +END + diff --git a/htdocs/view/svc_domain.cgi b/htdocs/view/svc_domain.cgi new file mode 100755 index 000000000..78ff6ac0b --- /dev/null +++ b/htdocs/view/svc_domain.cgi @@ -0,0 +1,76 @@ +#!/usr/bin/perl -Tw +# +# View svc_domain records +# +# Usage: svc_domain svcnum +# http://server.name/path/svc_domain.cgi?svcnum +# +# Note: Should be run setuid freeside as user nobody. +# +# ivan@voicenet.com 97-jan-6 +# +# rewrite ivan@sisd.com 98-mar-14 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); + +my($cgi) = new CGI::Base; +$cgi->get; +cgisuidsetup($cgi); + +#untaint svcnum +$QUERY_STRING =~ /^(\d+)$/; +my($svcnum)=$1; +my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$svcnum}); +die "Unknown svcnum" unless $svc_domain; +my($domain)=$svc_domain->domain; + +my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}); +my($pkgnum)=$cust_svc->getfield('pkgnum'); +my($cust_pkg,$custnum); +if ($pkgnum) { + $cust_pkg=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + $custnum=$cust_pkg->getfield('custnum'); +} + +my($part_svc)=qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } ); +die "Unkonwn svcpart" unless $part_svc; + +SendHeaders(); # one guess. +print < + + Domain View + + +

Domain View

+ +
+View this package (#$pkgnum) | +View this customer (#$custnum) | +Main menu

+ Service #$svcnum +
+END + +print "
"; +print "Service: ", $part_svc->svc, ""; +print "
"; + +print qq!Domain name $domain.!; +print qq!

View whois information.!; + +print "


"; + + #formatting + print < + +END + -- cgit v1.2.1 -- cgit v1.2.1 From c86ea4bb46792a5f175e9887c2e60d9bd3612056 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 3 Jun 1998 07:22:52 +0000 Subject: Initial revision --- bin/dbdef-create | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100755 bin/dbdef-create diff --git a/bin/dbdef-create b/bin/dbdef-create new file mode 100755 index 000000000..eb62c77e3 --- /dev/null +++ b/bin/dbdef-create @@ -0,0 +1,85 @@ +#!/usr/bin/perl -Tw +# +# create dbdef file for existing mySQL database (needs SHOW|DESCRIBE command +# not in Pg) based on fs-setup +# +# ivan@sisd.com 98-jun-2 + +use strict; +use DBI; +use FS::dbdef; +use FS::UID qw(adminsuidsetup datasrc); + +#needs to match FS::Record +my($dbdef_file) = "/var/spool/freeside/dbdef.". datasrc; + +my($dbh)=adminsuidsetup; + +my($tables_sth)=$dbh->prepare("SHOW TABLES"); +my($tables_rv)=$tables_sth->execute; + +my(@tables); +foreach ( @{$tables_sth->fetchall_arrayref} ) { + my($table)=${$_}[0]; + #print "TABLE\t$table\n"; + + my($index_sth)=$dbh->prepare("SHOW INDEX FROM $table"); + my($primary_key)=''; + my(%index,%unique); + for ( 1 .. $index_sth->execute ) { + my($row)=$index_sth->fetchrow_hashref; + if ( ${$row}{'Key_name'} eq "PRIMARY" ) { + $primary_key=${$row}{'Column_name'}; + next; + } + if ( ${$row}{'Non_unique'} ) { #index + push @{$index{${$row}{'Key_name'}}}, ${$row}{'Column_name'}; + } else { #unique + push @{$unique{${$row}{'Key_name'}}}, ${$row}{'Column_name'}; + } + } + + my(@index)=values %index; + my(@unique)=values %unique; + #print "\tPRIMARY KEY $primary_key\n"; + foreach (@index) { + #print "\tINDEX\t", join(', ', @{$_}), "\n"; + } + foreach (@unique) { + #print "\tUNIQUE\t", join(', ', @{$_}), "\n"; + } + + my($columns_sth)=$dbh->prepare("SHOW COLUMNS FROM $table"); + my(@columns); + for ( 1 .. $columns_sth->execute ) { + my($row)=$columns_sth->fetchrow_hashref; + #print "\t", ${$row}{'Field'}, "\n"; + ${$row}{'Type'} =~ /^(\w+)\(?([\d\,]+)?\)?( unsigned)?$/ + or die "Illegal type ${$row}{'Type'}\n"; + my($type,$length)=($1,$2); + my($null)=${$row}{'Null'}; + $null =~ s/YES/NULL/; + push @columns, new FS::dbdef_column ( + ${$row}{'Field'}, + $type, + $null, + $length, + ); + } + + #print "\n"; + push @tables, new FS::dbdef_table ( + $table, + $primary_key, + new FS::dbdef_unique (\@unique), + new FS::dbdef_index (\@index), + @columns, + ); + +} + +my($dbdef) = new FS::dbdef ( @tables ); + +#important +$dbdef->save($dbdef_file); + -- cgit v1.2.1 -- cgit v1.2.1 From c0215076a2f35c05af6194e9b8ad5600ccc83002 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 17 Jun 1998 02:03:08 +0000 Subject: Initial revision --- htdocs/misc/print-invoice.cgi | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 htdocs/misc/print-invoice.cgi diff --git a/htdocs/misc/print-invoice.cgi b/htdocs/misc/print-invoice.cgi new file mode 100755 index 000000000..084dcc1c4 --- /dev/null +++ b/htdocs/misc/print-invoice.cgi @@ -0,0 +1,57 @@ +#!/usr/bin/perl -Tw +# +# just a kludge for now, since this duplicates in a way it shouldn't stuff from +# Bill.pm (like $lpr) ivan@sisd.com 98-jun-16 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::Invoice; + +my($lpr) = "|lpr -h"; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +#untaint invnum +$QUERY_STRING =~ /^(\d*)$/; +my($invnum)=$1; +my($cust_bill)=qsearchs('cust_bill',{'invnum'=>$invnum}); +die "Can't find invoice!\n" unless $cust_bill; + + bless($cust_bill,"FS::Invoice"); + open(LPR,$lpr) or die "Can't open $lpr: $!"; + print LPR $cust_bill->print_text; #( date ) + close LPR + or die $! ? "Error closing $lpr: $!" + : "Exit status $? from $lpr"; + +my($custnum)=$cust_bill->getfield('custnum'); + +$cgi->redirect("../view/cust_main.cgi?$custnum#history"); + +sub idiot { + my($error)=@_; + CGI::Base::SendHeaders(); # one guess + print < + + Error printing invoice + + +
+

Error printing invoice

+
+ Your update did not occur because of the following error: +

$error + + +END + + exit; + +} + -- cgit v1.2.1 -- cgit v1.2.1 From addf9a15d0a2ade26f22b0157c390fe3f779d6dd Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 24 Jun 1998 06:59:16 +0000 Subject: Initial revision --- htdocs/misc/process/link.cgi | 73 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 htdocs/misc/process/link.cgi diff --git a/htdocs/misc/process/link.cgi b/htdocs/misc/process/link.cgi new file mode 100755 index 000000000..23fb05386 --- /dev/null +++ b/htdocs/misc/process/link.cgi @@ -0,0 +1,73 @@ +#!/usr/bin/perl -Tw +# +# process/link.cgi: link to existing customer (process form) +# +# ivan@voicenet.com 97-feb-5 +# +# rewrite ivan@sisd.com 98-mar-18 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# can also link on some other fields now (about time) ivan@sisd.com 98-jun-24 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::CGI qw(idiot); +use FS::UID qw(cgisuidsetup); +use FS::cust_svc; +use FS::Record qw(qsearchs); + +my($req)=new CGI::Request; # create form object +cgisuidsetup($req->cgi); + +#$req->import_names('R'); #import CGI variables into package 'R'; + +$req->param('pkgnum') =~ /^(\d+)$/; my($pkgnum)=$1; +$req->param('svcpart') =~ /^(\d+)$/; my($svcpart)=$1; + +$req->param('svcnum') =~ /^(\d*)$/; my($svcnum)=$1; +unless ( $svcnum ) { + my($part_svc) = qsearchs('part_svc',{'svcpart'=>$svcpart}); + my($svcdb) = $part_svc->getfield('svcdb'); + $req->param('link_field') =~ /^(\w+)$/; my($link_field)=$1; + my($svc_acct)=qsearchs($svcdb,{$link_field => $req->param('link_value') }); + idiot("$link_field not found!") unless $svc_acct; + $svcnum=$svc_acct->svcnum; +} + +my($old)=qsearchs('cust_svc',{'svcnum'=>$svcnum}); +die "svcnum not found!" unless $old; +my($new)=create FS::cust_svc ({ + 'svcnum' => $svcnum, + 'pkgnum' => $pkgnum, + 'svcpart' => $svcpart, +}); + +my($error); +$error = $new->replace($old); + +unless ($error) { + #no errors, so let's view this customer. + $req->cgi->redirect("../../view/cust_pkg.cgi?$pkgnum"); +} else { + CGI::Base::SendHeaders(); # one guess + print < + + Error + + +

+

Error

+
+ Your update did not occur because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and submit the form again. + + +END + +} + -- cgit v1.2.1 -- cgit v1.2.1 From 5987e58401569636f78af5125d0d2acab1ed6d2a Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 24 Jun 1998 07:27:58 +0000 Subject: Initial revision --- htdocs/misc/link.cgi | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 htdocs/misc/link.cgi diff --git a/htdocs/misc/link.cgi b/htdocs/misc/link.cgi new file mode 100755 index 000000000..d1db000ec --- /dev/null +++ b/htdocs/misc/link.cgi @@ -0,0 +1,72 @@ +#!/usr/bin/perl -Tw +# +# link: instead of adding a new account, link to an existing. (output form) +# +# Note: Should be run setuid freeside as user nobody +# +# ivan@voicenet.com 97-feb-5 +# +# rewrite ivan@sisd.com 98-mar-17 +# +# can also link on some other fields now (about time) ivan@sisd.com 98-jun-24 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); + +my(%link_field)=( + 'svc_acct' => 'username', + 'svc_domain' => 'domain', + 'svc_acct_sm' => '', + 'svc_charge' => '', + 'svc_wo' => '', +); + +my($cgi) = new CGI::Base; +$cgi->get; +cgisuidsetup($cgi); + +my($pkgnum,$svcpart); +foreach $_ (split(/-/,$QUERY_STRING)) { #get & untaint pkgnum & svcpart + $pkgnum=$1 if /^pkgnum(\d+)$/; + $svcpart=$1 if /^svcpart(\d+)$/; +} + +my($part_svc) = qsearchs('part_svc',{'svcpart'=>$svcpart}); +my($svc) = $part_svc->getfield('svc'); +my($svcdb) = $part_svc->getfield('svcdb'); +my($link_field) = $link_field{$svcdb}; + +CGI::Base::SendHeaders(); +print < + + Link to existing $svc account + + +

+

Link to existing $svc account

+

+
+END + +if ( $link_field ) { + print < + + $link_field of existing service: +END +} else { + print qq!Service # of existing service: !; +} + +print < + +

+ + + +END + -- cgit v1.2.1 -- cgit v1.2.1 From baba75d8568c152ee52ef5bab3c514c78468cf00 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 2 Jul 1998 09:31:09 +0000 Subject: Initial revision --- htdocs/edit/cust_pkg.cgi | 137 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100755 htdocs/edit/cust_pkg.cgi diff --git a/htdocs/edit/cust_pkg.cgi b/htdocs/edit/cust_pkg.cgi new file mode 100755 index 000000000..d7f143db4 --- /dev/null +++ b/htdocs/edit/cust_pkg.cgi @@ -0,0 +1,137 @@ +#!/usr/bin/perl -Tw +# +# cust_pkg.cgi: Add/edit packages (output form) +# +# this is for changing packages around, not editing things within the package +# +# Usage: cust_pkg.cgi custnum +# http://server.name/path/cust_pkg.cgi?custnum +# +# Note: Should be run setuid freeside as user nobody +# +# started with /sales/add/cust_pkg.cgi, which added packages +# ivan@voicenet.com 97-jan-5, 97-mar-21 +# +# Rewrote for new API +# ivan@voicenet.com 97-jul-7 +# +# FS::Search is no more, &cgisuidsetup needs $cgi, ivan@sisd.com 98-mar-7 +# +# Changes to allow page to work at a relative position in server +# Changed to display packages 2-wide in a table +# bmccane@maxbaud.net 98-apr-3 +# +# fixed a pretty cool bug from above which caused a visual glitch ivan@sisd.com +# 98-jun-1 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup getotaker); +use FS::Record qw(qsearch qsearchs); + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +my(%pkg,%comment); +foreach (qsearch('part_pkg', {})) { + $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); + $comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment'); +} + +#untaint custnum +$QUERY_STRING =~ /^(\d+)$/; +my($custnum)=$1; + +my($otaker)=&getotaker; + +SendHeaders(); +print < + + Add/Edit Packages + + +
+

Add/Edit Packages

+
+
+
+END + +#custnum +print qq!!; + +#current packages (except cancelled packages) +my(@cust_pkg) = grep ! $_->getfield('cancel'), + qsearch('cust_pkg',{'custnum'=>$custnum}); + +if (@cust_pkg) { + print <Current packages +These are packages the customer currently has. Select those packages you +wish to remove (if any).

+END + + my ($count) = 0 ; + print qq!
! ; + foreach (@cust_pkg) { + print qq!! if ($count ==0) ; + my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') ); + print qq!\n!, + #now you've got to admit this bug was pretty cool + qq!$pkgnum: $pkg{$pkgpart} - $comment{$pkgpart}\n!; + $count ++ ; + if ($count == 2) + { + $count = 0 ; + print qq!\n! ; + } + } + print qq!
!, + #qq!$pkgnum: $pkg{$pkgpart} - $comment{$pkgpart}
! ; + + print "
"; +} + +print <New packages +These are packages the customer can purchase. Specify the quantity to add +of each package.

+END + +my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum}); +my($agent)=qsearchs('agent',{'agentnum'=> $cust_main->agentnum }); + +my($type_pkgs); +my ($count) = 0 ; +print qq!
! ; +foreach $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) { + my($pkgpart)=$type_pkgs->pkgpart; + print qq!! if ($count == 0) ; + print < + + $pkgpart: $pkg{$pkgpart} - $comment{$pkgpart}\n +END + $count ++ ; + if ($count == 2) + { + print qq!\n! ; + $count = 0 ; + } +} +print qq!
! ; + +#otaker +print qq!\n!; + +#submit +print qq!

\n!; + +print < + + +END -- cgit v1.2.1 -- cgit v1.2.1 From 2f057aa80e429a549fb9fd9449eae8475c994796 Mon Sep 17 00:00:00 2001 From: ivan Date: Sat, 11 Jul 1998 03:49:08 +0000 Subject: Initial revision --- htdocs/view/cust_bill.cgi | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 htdocs/view/cust_bill.cgi diff --git a/htdocs/view/cust_bill.cgi b/htdocs/view/cust_bill.cgi new file mode 100755 index 000000000..96101d004 --- /dev/null +++ b/htdocs/view/cust_bill.cgi @@ -0,0 +1,79 @@ +#!/usr/bin/perl -Tw +# +# Usage: cust_bill.cgi invnum +# http://server.name/path/cust_bill.cgi?invnum +# +# Note: Should be run setuid freeside as user nobody. +# +# this is a quick & ugly hack which does little more than add some formatting to the ascii output from /dbin/print-invoice +# +# ivan@voicenet.com 96-dec-05 +# +# added navigation bar +# ivan@voicenet.com 97-jan-30 +# +# now uses Invoice.pm +# ivan@voicenet.com 97-jun-30 +# +# what to do if cust_bill search errors? +# ivan@voicenet.com 97-jul-7 +# +# s/FS::Search/FS::Record/; $cgisuidsetup($cgi); ivan@sisd.com 98-mar-14 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# also print 'printed' field ivan@sisd.com 98-jul-10 + +use strict; +use IO::File; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::Invoice; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +#untaint invnum +$QUERY_STRING =~ /^(\d+)$/; +my($invnum)=$1; + +my($cust_bill) = qsearchs('cust_bill',{'invnum'=>$invnum}); +die "Invoice #$invnum not found!" unless $cust_bill; +my($custnum) = $cust_bill->getfield('custnum'); + +my($printed) = $cust_bill->printed; + +SendHeaders(); # one guess. +print < + + Invoice View + + +
+

Invoice View

+ View this customer (#$custnum) | Main menu +

+ +
+ Enter payments (check/cash) against this invoice +
Reprint this invoice +

(Printed $printed times) +
+
+END
+
+bless($cust_bill,"FS::Invoice");
+print $cust_bill->print_text;
+
+	#formatting
+	print <
+  
+
+END
+
-- 
cgit v1.2.1

-- 
cgit v1.2.1


From 0cb9437cdf8f2de4f6e459ccfd83dc37e56e1f91 Mon Sep 17 00:00:00 2001
From: ivan 
Date: Sun, 12 Jul 1998 19:51:42 +0000
Subject: Initial revision

---
 htdocs/edit/part_svc.cgi | 148 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)
 create mode 100755 htdocs/edit/part_svc.cgi

diff --git a/htdocs/edit/part_svc.cgi b/htdocs/edit/part_svc.cgi
new file mode 100755
index 000000000..491c013fe
--- /dev/null
+++ b/htdocs/edit/part_svc.cgi
@@ -0,0 +1,148 @@
+#!/usr/bin/perl -Tw
+#
+# part_svc.cgi: Add/Edit service (output form)
+#
+# ivan@sisd.com 97-nov-14
+#
+# Changes to allow page to work at a relative position in server
+#       bmccane@maxbaud.net     98-apr-3
+#
+# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12
+
+use strict;
+use CGI::Base;
+use CGI::Carp qw(fatalsToBrowser);
+use FS::UID qw(cgisuidsetup);
+use FS::Record qw(qsearchs);
+use FS::part_svc qw(fields);
+use FS::CGI qw(header menubar);
+
+my($cgi) = new CGI::Base;
+$cgi->get;
+
+&cgisuidsetup($cgi);
+
+SendHeaders(); # one guess.
+
+my($part_svc,$action);
+if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing
+  $part_svc=qsearchs('part_svc',{'svcpart'=>$1});
+  $action='Edit';
+} else { #adding
+  $part_svc=create FS::part_svc {};
+  $action='Add';
+}
+my($hashref)=$part_svc->hashref;
+
+print header("$action Service Definition", menubar(
+  'Main Menu' => '../',
+  'View all services' => '../browse/part_svc.cgi',
+)), '';
+
+
+
+print qq!!,
+      "Service Part #", $hashref->{svcpart} ? $hashref->{svcpart} : "(NEW)";
+
+print <
+Service  
+Table    
Off"; + print qq!Default "; + print qq!Fixed "; + print qq!
!, + ""; + $ptmp=''; + } +} +print ""; + +print qq!\n

!; + +print < + + +END + -- cgit v1.2.1 -- cgit v1.2.1 From a0b0799f373f1af90d0472ff6a2055cb0aa1bc79 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 12 Jul 1998 20:18:52 +0000 Subject: Initial revision --- htdocs/edit/part_pkg.cgi | 102 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100755 htdocs/edit/part_pkg.cgi diff --git a/htdocs/edit/part_pkg.cgi b/htdocs/edit/part_pkg.cgi new file mode 100755 index 000000000..9fe739bb7 --- /dev/null +++ b/htdocs/edit/part_pkg.cgi @@ -0,0 +1,102 @@ +#!/usr/bin/perl -Tw +# +# part_pkg.cgi: Add/Edit package (output form) +# +# ivan@sisd.com 97-dec-10 +# +# Changes to allow page to work at a relative position in server +# Changed to display services 2-wide in table +# bmccane@maxbaud.net 98-apr-3 +# +# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::part_pkg; +use FS::pkg_svc; +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +my($part_pkg,$action); +if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing + $part_pkg=qsearchs('part_pkg',{'pkgpart'=>$1}); + $action='Edit'; +} else { #adding + $part_pkg=create FS::part_pkg {}; + $action='Add'; +} +my($hashref)=$part_pkg->hashref; + +print header("$action Package Definition", menubar( + 'Main Menu' => '../', + 'View all packages' => '../browse/part_pkg.cgi', +)), ''; + +print qq!!, + "Package Part #", $hashref->{pkgpart} ? $hashref->{pkgpart} : "(NEW)"; + +print < +Package (customer-visable) +Comment (customer-hidden) +Setup fee for this package +Recurring fee for this package +Frequency (months) of recurring fee + +
+ +Enter the quantity of each service this package includes.

+ + +END + +my($part_svc); +my($count) = 0 ; +foreach $part_svc ( qsearch('part_svc',{}) ) { + + my($svcpart)=$part_svc->getfield('svcpart'); + my($pkg_svc)=qsearchs('pkg_svc',{ + 'pkgpart' => $part_pkg->getfield('pkgpart'), + 'svcpart' => $svcpart, + }) || create FS::pkg_svc({ + 'pkgpart' => $part_pkg->getfield('pkgpart'), + 'svcpart' => $svcpart, + 'quantity' => 0, + }); + next unless $pkg_svc; + + print qq!! if $count == 0 ; + print qq!!, + qq!"; + $count ++ ; + if ($count == 2) + { + print qq!! ; + $count = 0 ; + } +} +print qq!! if ($count != 0) ; + +print "
Quan.ServiceQuan.Service
!, $part_svc->getfield('svc'), "
"; + +print qq!
!; + +print < + + +END + -- cgit v1.2.1 -- cgit v1.2.1 From f647714b103eb35a53419d170aac40660c851e0c Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 12 Jul 1998 20:29:24 +0000 Subject: Initial revision --- htdocs/edit/agent.cgi | 77 ++++++++++++++++++++++++++++++++++++++++++++++ htdocs/edit/agent_type.cgi | 75 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100755 htdocs/edit/agent.cgi create mode 100755 htdocs/edit/agent_type.cgi diff --git a/htdocs/edit/agent.cgi b/htdocs/edit/agent.cgi new file mode 100755 index 000000000..5bd116528 --- /dev/null +++ b/htdocs/edit/agent.cgi @@ -0,0 +1,77 @@ +#!/usr/bin/perl -Tw +# +# agent.cgi: Add/Edit agent (output form) +# +# ivan@sisd.com 97-dec-12 +# +# Changes to allow page to work at a relative position in server +# Changed 'type' to 'atype' because Pg6.3 reserves the type word +# bmccane@maxbaud.net 98-apr-3 +# +# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::agent; +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +my($agent,$action); +if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing + $agent=qsearchs('agent',{'agentnum'=>$1}); + $action='Edit'; +} else { #adding + $agent=create FS::agent {}; + $action='Add'; +} +my($hashref)=$agent->hashref; + +print header("$action Agent", menubar( + 'Main Menu' => '../', + 'View all agents' => '../browse/agent.cgi', +)), ''; + +print qq!!, + "Agent #", $hashref->{agentnum} ? $hashref->{agentnum} : "(NEW)"; + +print < +Agent +Agent type +Program (unimplemented) +
+END + +print qq!
!; + +print < + + +END + diff --git a/htdocs/edit/agent_type.cgi b/htdocs/edit/agent_type.cgi new file mode 100755 index 000000000..b9fff4530 --- /dev/null +++ b/htdocs/edit/agent_type.cgi @@ -0,0 +1,75 @@ +#!/usr/bin/perl -Tw +# +# agent_type.cgi: Add/Edit agent type (output form) +# +# ivan@sisd.com 97-dec-10 +# +# Changes to allow page to work at a relative position in server +# Changed 'type' to 'atype' because Pg6.3 reserves the type word +# bmccane@maxbaud.net 98-apr-3 +# +# use FS::CGI, added inline documentation ivan@sisd.com 98-jul-12 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::agent_type; +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +my($agent_type,$action); +if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing + $agent_type=qsearchs('agent_type',{'typenum'=>$1}); + $action='Edit'; +} else { #adding + $agent_type=create FS::agent_type {}; + $action='Add'; +} +my($hashref)=$agent_type->hashref; + +print header("$action Agent Type", menubar( + 'Main Menu' => '../', + 'View all agent types' => '../browse/agent_type.cgi', +)), ''; + +print qq!!, + "Agent Type #", $hashref->{typenum} ? $hashref->{typenum} : "(NEW)"; + +print <Type +

Select which packages agents of this type may sell to customers

+END + +my($part_pkg); +foreach $part_pkg ( qsearch('part_pkg',{}) ) { + print qq!
$agent_type->getfield('typenum'), + 'pkgpart' => $part_pkg->getfield('pkgpart'), + }) + ? 'CHECKED ' + : '', + qq!"VALUE="ON"> !,$part_pkg->getfield('pkg') + ; +} + +print qq!
!; + +print < + + +END + -- cgit v1.2.1 -- cgit v1.2.1 From 8ae047bd7c16f16ace5acfa1098392b56835d24c Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 13 Jul 1998 17:52:51 +0000 Subject: Initial revision --- htdocs/edit/cust_main.cgi | 214 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100755 htdocs/edit/cust_main.cgi diff --git a/htdocs/edit/cust_main.cgi b/htdocs/edit/cust_main.cgi new file mode 100755 index 000000000..14556010c --- /dev/null +++ b/htdocs/edit/cust_main.cgi @@ -0,0 +1,214 @@ +#!/usr/bin/perl -Tw +# +# cust_main.cgi: Edit a customer (output form) +# +# Usage: cust_main.cgi custnum +# http://server.name/path/cust_main.cgi?custnum +# +# Note: Should be run setuid freeside as user nobody. +# +# ivan@voicenet.com 96-nov-29 -> 96-dec-04 +# +# Blank custnum for new customer. +# ivan@voicenet.com 96-dec-16 +# +# referral defaults to blank, to force people to pick something +# ivan@voicenet.com 97-jun-4 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-28 +# +# new customer is null, not '#' +# otaker gotten from &getotaker instead of $ENV{REMOTE_USER} +# ivan@sisd.com 97-nov-12 +# +# cgisuidsetup($cgi); +# no need for old_ fields. +# now state+county is a select field (took out PA hack) +# used autoloaded $cust_main->field methods +# ivan@sisd.com 97-dec-17 +# +# fixed quoting problems ivan@sisd.com 98-feb-23 +# +# paydate sql update ivan@sisd.com 98-mar-5 +# +# Changes to allow page to work at a relative position in server +# Changed 'day' to 'daytime' because Pg6.3 reserves the day word +# Added test for paydate in mm-dd-yyyy format for Pg6.3 default format +# bmccane@maxbaud.net 98-apr-3 +# +# fixed one missed day->daytime ivan@sisd.com 98-jul-13 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup getotaker); +use FS::Record qw(qsearch qsearchs); +use FS::cust_main; + +my($cgi) = new CGI::Base; +$cgi->get; + +cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +#get record +my($custnum,$action,$cust_main); +if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing + $custnum=$1; + $cust_main = qsearchs('cust_main',{'custnum'=>$custnum}); + $action='Edit'; +} else { + $custnum=''; + $cust_main = create FS::cust_main ( {} ); + $cust_main->setfield('otaker',&getotaker); + $cust_main->setfield('country','US'); + $action='Add'; +} + +print < + + Customer $action + + +
+

Customer $action

+
+ +
+END
+
+print qq!!,
+      qq!Customer #!;
+print $custnum ? $custnum : " (NEW)" , "";
+
+#agentnum
+my($agentnum)=$cust_main->agentnum || 1; #set to first agent by default
+my(@agents) = qsearch('agent',{});
+print qq!\n\nAgent # ";
+
+#referral
+#unless ($custnum) {
+  my($refnum)=$cust_main->refnum || 0; #to avoid "arguement not numeric" error
+  my(@referrals) = qsearch('part_referral',{});
+  print qq!\nReferral ";
+#}
+
+my($last,$first,$ss,$company,$address1,$address2,$city)=(
+  $cust_main->last,
+  $cust_main->first,
+  $cust_main->ss,
+  $cust_main->company,
+  $cust_main->address1,
+  $cust_main->address2,
+  $cust_main->city,
+);
+
+print < (first)  SS# 
+Company 
+Address 
+        
+City   State (county) ";
+
+my($zip,$country,$daytime,$night,$fax)=(
+  $cust_main->zip,
+  $cust_main->country,
+  $cust_main->daytime,
+  $cust_main->night,
+  $cust_main->fax,
+);
+
+print <
+Country: $country
+
+Phone (daytime)  (night)  (fax)
+
+END
+
+my(%payby)=(
+  'CARD' => "Credit card    ",
+  'BILL' => "Billing    ",
+  'COMP' => "Complimentary",
+);
+for (qw(CARD BILL COMP)) {
+  print qq!payby eq "$_");
+  print qq!>$payby{$_}!;
+}
+
+
+my($payinfo,$payname,$otaker)=(
+  $cust_main->payinfo,
+  $cust_main->payname,
+  $cust_main->otaker,
+);
+
+my($paydate);
+if ( $cust_main->paydate =~ /^(\d{4})-(\d{2})-\d{2}$/ ) {
+  $paydate="$2/$1"
+} elsif ( $cust_main->paydate =~ /^(\d{2})-\d{2}-(\d{4}$)/ ) {
+  $paydate="$1/$2"
+}
+else {
+  $paydate='';
+}
+
+print <
+END
+
+print qq!Exp. date (MM/YY or MM/YYYY)    Billing name \ntax eq "Y";
+print qq!> Tax Exempt!;
+
+print <$otaker
+
+END + +print qq!
!; + +print < + + +END + -- cgit v1.2.1 -- cgit v1.2.1 From 0adf9a70939b9999f7a871557e621aa22a714ea9 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 13 Jul 1998 19:32:18 +0000 Subject: Initial revision --- htdocs/edit/svc_acct.cgi | 191 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100755 htdocs/edit/svc_acct.cgi diff --git a/htdocs/edit/svc_acct.cgi b/htdocs/edit/svc_acct.cgi new file mode 100755 index 000000000..61d0fdc28 --- /dev/null +++ b/htdocs/edit/svc_acct.cgi @@ -0,0 +1,191 @@ +#!/usr/bin/perl -Tw +# +# svc_acct.cgi: Add/edit account (output form) +# +# Usage: svc_acct.cgi {svcnum} | pkgnum{pkgnum}-svcpart{svcpart} +# http://server.name/path/svc_acct.cgi? {svcnum} | pkgnum{pkgnum}-svcpart{svcpart} +# +# Note: Should be run setuid freeside as user nobody +# +# ivan@voicenet.com 96-dec-18 +# +# rewrite ivan@sisd.com 98-mar-8 +# +# Changes to allow page to work at a relative position in server +# Changed 'password' to '_password' because Pg6.3 reserves the password word +# bmccane@maxbaud.net 98-apr-3 +# +# use conf/shells and dbdef username length ivan@sisd.com 98-jul-13 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use FS::UID qw(cgisuidsetup getotaker); +use FS::Record qw(qsearch qsearchs); +use FS::svc_acct qw(fields); + +my($shells)="/var/spool/freeside/conf/shells"; +open(SHELLS,$shells) or die "Can't open $shells: $!"; +my(@shells)=map { + /^([\/\w]*)$/ or die "Illegal shell in conf/shells!"; + $1; +} grep $_ !~ /^#/, ; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +my($action,$svcnum,$svc_acct,$pkgnum,$svcpart,$part_svc); + +if ( $QUERY_STRING =~ /^(\d+)$/ ) { #editing + + $svcnum=$1; + $svc_acct=qsearchs('svc_acct',{'svcnum'=>$svcnum}) + or die "Unknown (svc_acct) svcnum!"; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum=$cust_svc->pkgnum; + $svcpart=$cust_svc->svcpart; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $action="Edit"; + +} else { #adding + + $svc_acct=create FS::svc_acct({}); + + foreach $_ (split(/-/,$QUERY_STRING)) { + $pkgnum=$1 if /^pkgnum(\d+)$/; + $svcpart=$1 if /^svcpart(\d+)$/; + } + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $svcnum=''; + + #set gecos + my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + if ($cust_pkg) { + my($cust_main)=qsearchs('cust_main',{'custnum'=> $cust_pkg->custnum } ); + $svc_acct->setfield('finger', + $cust_main->getfield('first') . " " . $cust_main->getfield('last') + ) ; + } + + #set fixed and default fields from part_svc + my($field); + foreach $field ( fields('svc_acct') ) { + if ( $part_svc->getfield('svc_acct__'. $field. '_flag') ne '' ) { + $svc_acct->setfield($field,$part_svc->getfield('svc_acct__'. $field) ); + } + } + + $action="Add"; + +} + +my($svc)=$part_svc->getfield('svc'); + +my($otaker)=getotaker; + +my($username,$password)=( + $svc_acct->username, + $svc_acct->_password ? "*HIDDEN*" : '', +); + +my($ulen)=$svc_acct->dbdef_table->column('username')->length; +my($ulen2)=$ulen+2; + +SendHeaders(); +print < + + $action $svc account + + +
+

$action $svc account

+

+ + + + +Username: + +
Password: + +(blank to generate) +END + +#pop +my($popnum)=$svc_acct->popnum || 0; +if ( $part_svc->svc_acct__popnum_flag eq "F" ) { + print qq!!; +} else { + print qq!
POP: "; +} + +my($uid,$gid,$finger,$dir)=( + $svc_acct->uid, + $svc_acct->gid, + $svc_acct->finger, + $svc_acct->dir, +); + +print < + +
GECOS: + +END + +my($shell)=$svc_acct->shell; +if ( $part_svc->svc_acct__shell_flag eq "F" ) { + print qq!!; +} else { + print qq!
Shell: "; +} + +my($quota,$slipip)=( + $svc_acct->quota, + $svc_acct->slipip, +); + +print qq!!; + +if ( $part_svc->svc_acct__slipip_flag eq "F" ) { + print qq!!; +} else { + print qq!
IP: !; +} + +#submit +print qq!

!; + +print < + + +END + + -- cgit v1.2.1 -- cgit v1.2.1 From 127b2e52cd2d6a9fd918be8ca7202b560d669e0a Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 17 Jul 1998 07:43:57 +0000 Subject: Initial revision --- bin/svc_acct_sm.import | 252 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100755 bin/svc_acct_sm.import diff --git a/bin/svc_acct_sm.import b/bin/svc_acct_sm.import new file mode 100755 index 000000000..10d7e4c20 --- /dev/null +++ b/bin/svc_acct_sm.import @@ -0,0 +1,252 @@ +#!/usr/bin/perl -Tw +# +# ivan@sisd.com 98-mar-9 +# +# generalized svcparts ivan@sisd.com 98-mar-23 + +# You really need to enable ssh into a shell machine as this needs to rename +# .qmail-extension files. +# +# now an interactive script ivan@sisd.com 98-jun-30 +# +# has an (untested) section for sendmail, s/warn/die/g and generates a program +# to run on your mail machine _later_ instead of ssh'ing for each user +# ivan@sisd.com 98-jul-13 + +use strict; +use vars qw(%d_part_svc %m_part_svc); +use FS::SSH qw(iscp); +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::svc_acct_sm; +use FS::svc_domain; + +adminsuidsetup; + +#my($spooldir)="/var/spool/freeside/export"; +my($spooldir)="unix"; + +my(%mta) = ( + 1 => "qmail", + 2 => "sendmail", +); + +### + +%d_part_svc = + map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_domain'}); +%m_part_svc = + map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct_sm'}); + +print "\n\n", + ( join "\n", map "$_: ".$d_part_svc{$_}->svc, sort keys %d_part_svc ), + "\n\nEnter part number for domains: "; +my($domain_svcpart)=&getvalue; + +print "\n\n", + ( join "\n", map "$_: ".$m_part_svc{$_}->svc, sort keys %m_part_svc ), + "\n\nEnter part number for mail aliases: "; +my($mailalias_svcpart)=&getvalue; + +print "\n\n", <); + chop $x; + $x; +} + +print "\n\n"; + +### + +$FS::svc_domain::whois_hack=1; +$FS::svc_acct_sm::nossh_hack=1; + +if ( $mta{$mta} eq "qmail" ) { + open(RCPTHOSTS,"<$spooldir/rcpthosts.import") + or die "Can't open $spooldir/rcpthosts.import: $!"; +} elsif ( $mta{$mta} eq "sendmail" ) { + open(RCPTHOSTS,"<$spooldir/sendmail.cw.import") + or die "Can't open $spooldir/sendmail.cw.import: $!"; +} else { + die "Unknown MTA!\n"; +} + +my(%svcnum); + +while () { + next if /^(#|$)/; + /^\.?([\w\-\.]+)$/ + #or do { warn "Strange rcpthosts/sendmail.cw line: $_"; next; }; + or die "Strange rcpthosts/sendmail.cw line: $_"; + my $domain = $1; + my($svc_domain); + unless ( $svc_domain = qsearchs('svc_domain', {'domain'=>$domain} ) ) { + $svc_domain = create FS::svc_domain ({ + 'domain' => $domain, + 'svcpart' => $domain_svcpart, + 'action' => 'N', + }); + my $error = $svc_domain->insert; + #warn $error if $error; + die $error if $error; + } + $svcnum{$domain}=$svc_domain->svcnum; +} +close RCPTHOSTS; + +#these two loops have enough similar parts they should probably be merged +if ( $mta{$mta} eq "qmail" ) { + + open(VD_FIX,">$spooldir/virtualdomains.FIX"); + print VD_FIX "#!/usr/bin/perl\n"; + + open(VIRTUALDOMAINS,"<$spooldir/virtualdomains.import") + or die "Can't open $spooldir/virtualdomains.import: $!"; + while () { + next if /^#/; + /^\.?([\w\-\.]+):(\w+)(\-([\w\-\.]+))?$/ + #or do { warn "Strange virtualdomains line: $_"; next; }; + or die "Strange virtualdomains line: $_"; + my($domain,$username,$dash_ext,$extension)=($1,$2,$3,$4); + $dash_ext ||= ''; + $extension ||= ''; + my($svc_acct)=qsearchs('svc_acct',{'username'=>$username}); + unless ( $svc_acct ) { + #warn "Unknown user $username in virtualdomains; skipping\n"; + #die "Unknown user $username in virtualdomains; skipping\n"; + next; + } + if ( $domain ne $extension ) { + #warn "virtualdomains line $domain:$username$dash_ext changed to $domain:$username-$domain\n"; + my($dir)=$svc_acct->dir; + my($qdomain)=$domain; + $qdomain =~ s/\./:/g; #see manpage for 'dot-qmail': EXTENSION ADDRESSES + #example to move .qmail files for virtual domains to their new location + #dry run + #issh("root\@$shellmachine",'perl -e \'foreach $a (<'. $dir. '/.qmail'. $dash_ext. '-*>) { $old=$a; $a =~ s/\\.qmail'. $dash_ext. '\\-/\\.qmail\\-'. $qdomain. '\\-/; print " $old -> $a\n"; }\''); + #the real thing + #issh("root\@$shellmachine",'perl -e \'foreach $a (<'. $dir. '/.qmail'. $dash_ext. '-*>) { $old=$a; $a =~ s/\\.qmail'. $dash_ext. '\\-/\\.qmail\\-'. $qdomain. '\\-/; rename $old, $a; }\''); + print VD_FIX <) { + \$old = \$file; + \$file =~ s/\.qmail$dash_ext\-/\.qmail\-$qdomain\-/; + rename \$old, \$file; +} +END + } + + unless ( exists $svcnum{$domain} ) { + my($svc_domain) = create FS::svc_domain ({ + 'domain' => $domain, + 'svcpart' => $domain_svcpart, + 'action' => 'N', + }); + my $error = $svc_domain->insert; + #warn $error if $error; + die $error if $error; + $svcnum{$domain}=$svc_domain->svcnum; + } + + my($svc_acct_sm)=create FS::svc_acct_sm ({ + 'domsvc' => $svcnum{$domain}, + 'domuid' => $svc_acct->uid, + 'domuser' => '*', + 'svcpart' => $mailalias_svcpart, + }); + my($error)=''; + $error=$svc_acct_sm->insert; + #warn $error if $error; + die $error, ", domain $domain" if $error; + } + close VIRTUALDOMAINS; + close VD_FIX; + +} elsif ( $mta{$mta} eq "sendmail" ) { + + open(VIRTUSERTABLE,"<$spooldir/virtusertable.import") + or die "Can't open $spooldir/virtusertable.import: $!"; + while () { + next if /^#/; #comments? + /^([\w\-\.]+)?\@([\w\-\.]+)\t([\w\-\.]+)$/ + #or do { warn "Strange virtusertable line: $_"; next; }; + or die "Strange virtusertable line: $_"; + my($domuser,$domain,$username)=($1,$2,$3); + my($svc_acct)=qsearchs('svc_acct',{'username'=>$username}); + unless ( $svc_acct ) { + #warn "Unknown user $username in virtusertable"; + die "Unknown user $username in virtusertable"; + next; + } + my($svc_acct_sm)=create FS::svc_acct_sm ({ + 'domsvc' => $svcnum{$domain}, + 'domuid' => $svc_acct->uid, + 'domuser' => $domuser || '*', + 'svcpart' => $mailalias_svcpart, + }); + my($error)=''; + $error=$svc_acct_sm->insert; + #warn $error if $error; + die $error if $error; + } + close VIRTUSERTABLE; + +} else { + die "Unknown MTA!\n"; +} + +#open(RECIPIENTMAP,"<$spooldir/recipientmap.import"); +#close RECIPIENTMAP; + +print "\n\n", < Date: Fri, 17 Jul 1998 08:52:05 +0000 Subject: Initial revision --- htdocs/search/cust_pkg.cgi | 122 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100755 htdocs/search/cust_pkg.cgi diff --git a/htdocs/search/cust_pkg.cgi b/htdocs/search/cust_pkg.cgi new file mode 100755 index 000000000..967068f5e --- /dev/null +++ b/htdocs/search/cust_pkg.cgi @@ -0,0 +1,122 @@ +#!/usr/bin/perl -Tw +# +# cust_pkg.cgi: search/browse for packages +# +# based on search/svc_acct.cgi ivan@sisd.com 98-jul-17 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header idiot); + +my($req)=new CGI::Request; +&cgisuidsetup($req->cgi); + +my(@cust_pkg,$sortby); + +my($query)=$req->cgi->var('QUERY_STRING'); +#this tree is a little bit redundant +if ( $query eq 'pkgnum' ) { + $sortby=\*pkgnum_sort; + @cust_pkg=qsearch('cust_pkg',{}); +} elsif ( $query eq 'APKG_pkgnum' ) { + $sortby=\*pkgnum_sort; + + #perhaps this should go in cust_pkg as a qsearch-like constructor? + my($cust_pkg); + foreach $cust_pkg (qsearch('cust_pkg',{})) { + my($flag)=0; + my($pkg_svc); + PKG_SVC: + foreach $pkg_svc (qsearch('pkg_svc',{ 'pkgpart' => $cust_pkg->pkgpart })) { + if ( $pkg_svc->quantity + > scalar(qsearch('cust_svc',{ + 'pkgnum' => $cust_pkg->pkgnum, + 'svcpart' => $pkg_svc->svcpart, + })) + ) + { + $flag=1; + last PKG_SVC; + } + } + push @cust_pkg, $cust_pkg if $flag; + } +} else { + die "Empty QUERY_STRING!"; +} + +if ( scalar(@cust_pkg) == 1 ) { + my($pkgnum)=$cust_pkg[0]->pkgnum; + $req->cgi->redirect("../view/cust_pkg.cgi?$pkgnum"); + exit; +} elsif ( scalar(@cust_pkg) == 0 ) { #error + &idiot("No packages found"); + exit; +} else { + my($total)=scalar(@cust_pkg); + CGI::Base::SendHeaders(); # one guess + print header('Package Search Results',''), < + + Package # + Customer # + Name + Company + +END + + my($lines)=16; + my($lcount)=$lines; + my(%saw,$cust_pkg); + foreach $cust_pkg ( + sort $sortby grep(!$saw{$_->pkgnum}++, @cust_pkg) + ) { + my($cust_main)=qsearchs('cust_main',{'custnum'=>$cust_pkg->custnum}); + my($pkgnum,$custnum,$name,$company)=( + $cust_pkg->pkgnum, + $cust_main->custnum, + $cust_main->last. ', '. $cust_main->first, + $cust_main->company, + ); + print < + $pkgnum + $custnum + $name + $company + +END + if ($lcount-- == 0) { # lots of little tables instead of one big one + $lcount=$lines; + print < + + + + + + + +END + } + } + + print < + + + +END + exit; + +} + +sub pkgnum_sort { + $a->getfield('pkgnum') <=> $b->getfield('pkgnum'); +} + -- cgit v1.2.1 -- cgit v1.2.1 From 50f9de542e60bafe915546a05414d00a3ff1d583 Mon Sep 17 00:00:00 2001 From: ivan Date: Sat, 18 Jul 1998 05:42:55 +0000 Subject: Initial revision --- htdocs/search/svc_domain.cgi | 139 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100755 htdocs/search/svc_domain.cgi diff --git a/htdocs/search/svc_domain.cgi b/htdocs/search/svc_domain.cgi new file mode 100755 index 000000000..d5277037b --- /dev/null +++ b/htdocs/search/svc_domain.cgi @@ -0,0 +1,139 @@ +#!/usr/bin/perl -Tw +# +# svc_domain.cgi: Search for domains (process form) +# +# Usage: post form to: +# http://server.name/path/svc_domain.cgi +# +# Note: Should be run setuid freeside as user nobody. +# +# ivan@voicenet.com 97-mar-5 +# +# rewrite ivan@sisd.com 98-mar-14 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# display total, use FS::CGI now does browsing too ivan@sisd.com 98-jul-17 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header idiot); + +my($req)=new CGI::Request; +&cgisuidsetup($req->cgi); + +my(@svc_domain); +my($sortby); + +my($query)=$req->cgi->var('QUERY_STRING'); +if ( $query eq 'svcnum' ) { + $sortby=\*svcnum_sort; + @svc_domain=qsearch('svc_domain',{}); +} elsif ( $query eq 'domain' ) { + $sortby=\*domain_sort; + @svc_domain=qsearch('svc_domain',{}); +} elsif ( $query eq 'UN_svcnum' ) { + $sortby=\*svcnum_sort; + @svc_domain = grep qsearchs('cust_svc',{ + 'svcnum' => $_->svcnum, + 'pkgnum' => '', + }), qsearch('svc_domain',{}); +} elsif ( $query eq 'UN_domain' ) { + $sortby=\*domain_sort; + @svc_domain = grep qsearchs('cust_svc',{ + 'svcnum' => $_->svcnum, + 'pkgnum' => '', + }), qsearch('svc_domain',{}); +} else { + $req->param('domain') =~ /^([\w\-\.]+)$/; + my($domain)=$1; + push @svc_domain, qsearchs('svc_domain',{'domain'=>$domain}); +} + +if ( scalar(@svc_domain) == 1 ) { + $req->cgi->redirect("../view/svc_domain.cgi?". $svc_domain[0]->svcnum); + exit; +} elsif ( scalar(@svc_domain) == 0 ) { + idiot "No matching domains found!\n"; + exit; +} else { + CGI::Base::SendHeaders(); # one guess + + my($total)=scalar(@svc_domain); + CGI::Base::SendHeaders(); # one guess + print header("Domain Search Results",''), < + + + + + +END + + my($lines)=16; + my($lcount)=$lines; + my(%saw,$svc_domain); + foreach $svc_domain ( + sort $sortby grep(!$saw{$_->svcnum}++, @svc_domain) + ) { + my($svcnum,$domain)=( + $svc_domain->svcnum, + $svc_domain->domain, + ); + my($malias); + if ( qsearch('svc_acct_sm',{'domsvc'=>$svcnum}) ) { + $malias=( + qq||. + qq||. + qq||. + qq||. + qq|| + ); + } else { + $malias=''; + } + print < + + + + +END + if ($lcount-- == 0) { # lots of little tables instead of one big one + $lcount=$lines; + print < +
Package #Customer #NameCompany +
Service #Domain
$svcnum$domain$malias
+ + + + + +END + } + } + + print < + + + +END + +} + +sub svcnum_sort { + $a->getfield('svcnum') <=> $b->getfield('svcnum'); +} + +sub domain_sort { + $a->getfield('domain') cmp $b->getfield('doimain'); +} + + -- cgit v1.2.1 -- cgit v1.2.1 From a376998b570ac32a2b77b271cdb4946f18ebd4ac Mon Sep 17 00:00:00 2001 From: ivan Date: Sat, 18 Jul 1998 07:11:08 +0000 Subject: Initial import into CVS --- etc/domain-template.txt | 231 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 etc/domain-template.txt diff --git a/etc/domain-template.txt b/etc/domain-template.txt new file mode 100644 index 000000000..8e4983ce2 --- /dev/null +++ b/etc/domain-template.txt @@ -0,0 +1,231 @@ +[ URL ftp://rs.internic.net/templates/domain-template.txt ] [ 03/98 ] + +******* Please DO NOT REMOVE Version Number or Sections A-Q ******** + +Domain Version Number: 4.0 + +******* Email completed agreement to hostmaster@internic.net ******* + + NETWORK SOLUTIONS, INC. + + DOMAIN NAME REGISTRATION AGREEMENT + + +A. Introduction. This domain name registration agreement +("Registration Agreement") is submitted to NETWORK SOLUTIONS, INC. +("NSI") for the purpose of applying for and registering a domain name +on the Internet. If this Registration Agreement is accepted by NSI, +and a domain name is registered in NSI's domain name database and +assigned to the Registrant, Registrant ("Registrant") agrees to be +bound by the terms of this Registration Agreement and the terms of +NSI's Domain Name Dispute Policy ("Dispute Policy") which is +incorporated herein by reference and made a part of this Registration +Agreement. This Registration Agreement shall be accepted at the +offices of NSI. + +B. Fees and Payments. + +1) Registration or renewal (re-registration) date through March 31, 1998: +Registrant agrees to pay a registration fee of One Hundred United States +Dollars (US$100) as consideration for the registration of each new domain +name or Fifty United States Dollars (US$50) to renew (re-register) an +existing registration. +2) Registration or renewal date on and after April 1, 1998: Registrant +agrees to pay a registration fee of Seventy United States Dollars (US$70) +as consideration for the registration of each new domain name or the +applicable renewal (re-registration) fee (currently Thirty-Five United +States Dollars (US$35)) at the time of renewal (re-registration). +3) Period of Service: The non-refundable fee covers a period of two (2) +years for each new registration, and one (1) year for each renewal, +and includes any permitted modification(s) to the domain name record +during the covered period. +4) Payment: Payment is due to Network Solutions within thirty (30) +days from the date of the invoice. + +C. Dispute Policy. Registrant agrees, as a condition to +submitting this Registration Agreement, and if the Registration +Agreement is accepted by NSI, that the Registrant shall be bound by +NSI's current Dispute Policy. The current version of the Dispute +Policy may be found at the InterNIC Registration Services web site: +"http://www.netsol.com/rs/dispute-policy.html". + +D. Dispute Policy Changes or Modifications. Registrant agrees +that NSI, in its sole discretion, may change or modify the Dispute +Policy, incorporated by reference herein, at any time. Registrant +agrees that Registrant's maintaining the registration of a domain name +after changes or modifications to the Dispute Policy become effective +constitutes Registrant's continued acceptance of these changes or +modifications. Registrant agrees that if Registrant considers any such +changes or modifications to be unacceptable, Registrant may request +that the domain name be deleted from the domain name database. + +E. Disputes. Registrant agrees that, if the registration of its +domain name is challenged by any third party, the Registrant will be +subject to the provisions specified in the Dispute Policy. + +F. Agents. Registrant agrees that if this Registration Agreement +is completed by an agent for the Registrant, such as an ISP or +Administrative Contact/Agent, the Registrant is nonetheless bound as a +principal by all terms and conditions herein, including the Dispute +Policy. + +G. Limitation of Liability. Registrant agrees that NSI shall have +no liability to the Registrant for any loss Registrant may incur in +connection with NSI's processing of this Registration Agreement, in +connection with NSI's processing of any authorized modification to the +domain name's record during the covered period, as a result of the +Registrant's ISP's failure to pay either the initial registration fee +or renewal fee, or as a result of the application of the provisions of +the Dispute Policy. Registrant agrees that in no event shall the +maximum liability of NSI under this Agreement for any matter exceed +Five Hundred United States Dollars (US$500). + +H. Indemnity. Registrant agrees, in the event the Registration +Agreement is accepted by NSI and a subsequent dispute arises with any +third party, to indemnify and hold NSI harmless pursuant to the terms +and conditions contained in the Dispute Policy. + +I. Breach. Registrant agrees that failure to abide by any +provision of this Registration Agreement or the Dispute Policy may be +considered by NSI to be a material breach and that NSI may provide a +written notice, describing the breach, to the Registrant. If, within +thirty (30) days of the date of mailing such notice, the Registrant +fails to provide evidence, which is reasonably satisfactory to NSI, +that it has not breached its obligations, then NSI may delete +Registrant's registration of the domain name. Any such breach by a +Registrant shall not be deemed to be excused simply because NSI did +not act earlier in response to that, or any other, breach by the +Registrant. + +J. No Guaranty. Registrant agrees that, by registration of a +domain name, such registration does not confer immunity from objection +to either the registration or use of the domain name. + +K. Warranty. Registrant warrants by submitting this Registration +Agreement that, to the best of Registrant's knowledge and belief, the +information submitted herein is true and correct, and that any future +changes to this information will be provided to NSI in a timely manner +according to the domain name modification procedures in place at that +time. Breach of this warranty will constitute a material breach. + +L. Revocation. Registrant agrees that NSI may delete a +Registrant's domain name if this Registration Agreement, or subsequent +modification(s) thereto, contains false or misleading information, or +conceals or omits any information NSI would likely consider material +to its decision to approve this Registration Agreement. + +M. Right of Refusal. NSI, in its sole discretion, reserves the +right to refuse to approve the Registration Agreement for any +Registrant. Registrant agrees that the submission of this Registration +Agreement does not obligate NSI to accept this Registration Agreement. +Registrant agrees that NSI shall not be liable for loss or damages +that may result from NSI's refusal to accept this Registration +Agreement. + +N. Severability. Registrant agrees that the terms of this +Registration Agreement are severable. If any term or provision is +declared invalid, it shall not affect the remaining terms or +provisions which shall continue to be binding. + +O. Entirety. Registrant agrees that this Registration Agreement +and the Dispute Policy is the complete and exclusive agreement between +Registrant and NSI regarding the registration of Registrant's domain +name. This Registration Agreement and the Dispute Policy supersede all +prior agreements and understandings, whether established by custom, +practice, policy, or precedent. + +P. Governing Law. Registrant agrees that this Registration +Agreement shall be governed in all respects by and construed in +accordance with the laws of the Commonwealth of Virginia, United +States of America. By submitting this Registration Agreement, +Registrant consents to the exclusive jurisdiction and venue of the +United States District Court for the Eastern District of Virginia, +Alexandria Division. If there is no jurisdiction in the United States +District Court for the Eastern District of Virginia, Alexandria +Division, then jurisdiction shall be in the Circuit Court of Fairfax +County, Fairfax, Virginia. + +Q. This is Domain Name Registration Agreement Version +Number 4.0. This Registration Agreement is only for registrations +under top-level domains: COM, ORG, NET, and EDU. By completing +and submitting this Registration Agreement for consideration and +acceptance by NSI, the Registrant agrees that he/she has read and +agrees to be bound by A through P above. + + +Authorization +0a. (N)ew (M)odify (D)elete....:###action### +0b. Auth Scheme................: +0c. Auth Info..................: + +1. Comments...................:###purpose### + +2. Complete Domain Name.......:###domain### + +Organization Using Domain Name + +3a. Organization Name..........:###company### +###LOOP### +3b. Street Address.............:###address### +###ENDLOOP### +3c. City.......................:###city### +3d. State......................:###state### +3e. Postal Code................:###zip### +3f. Country....................:###country### + +Administrative Contact +4a. NIC Handle (if known)......: +4b. (I)ndividual (R)ole........:I +4c. Name (Last, First).........:###last###, ###first### +4d. Organization Name..........:###company### +###LOOP### +4e. Street Address.............:###address### +###ENDLOOP### +4f. City.......................:###city### +4g. State......................:###state### +4h. Postal Code................:###zip### +4i. Country....................:###country### +4j. Phone Number...............:###daytime### +4k. Fax Number.................:###fax### +4l. E-Mailbox..................:###email### + +Technical Contact +5a. NIC Handle (if known)......:###tech_contact### +5b. (I)ndividual (R)ole........: +5c. Name (Last, First).........: +5d. Organization Name..........: +5e. Street Address.............: +5f. City.......................: +5g. State......................: +5h. Postal Code................: +5i. Country....................: +5j. Phone Number...............: +5k. Fax Number.................: +5l. E-Mailbox..................: + +Billing Contact +6a. NIC Handle (if known)......: +6b. (I)ndividual (R)ole........: +6c. Name (Last, First).........: +6d. Organization Name..........: +6e. Street Address.............: +6f. City.......................: +6g. State......................: +6h. Postal Code................: +6i. Country....................: +6j. Phone Number...............: +6k. Fax Number.................: +6l. E-Mailbox..................: + +Prime Name Server +7a. Primary Server Hostname....:###primary### +7b. Primary Server Netaddress..:###primary_ip### + +Secondary Name Server(s) +###LOOP### +8a. Secondary Server Hostname..:###secondary### +8b. Secondary Server Netaddress:###secondary_ip### +###ENDLOOP### + +END OF AGREEMENT + -- cgit v1.2.1 From f0a04c17c324219c6edb893821b4bd7f3dcbfd77 Mon Sep 17 00:00:00 2001 From: ivan Date: Sat, 18 Jul 1998 07:24:10 +0000 Subject: Initial revision --- htdocs/edit/svc_domain.cgi | 120 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100755 htdocs/edit/svc_domain.cgi diff --git a/htdocs/edit/svc_domain.cgi b/htdocs/edit/svc_domain.cgi new file mode 100755 index 000000000..0717a2c09 --- /dev/null +++ b/htdocs/edit/svc_domain.cgi @@ -0,0 +1,120 @@ +#!/usr/bin/perl -Tw +# +# svc_domain.cgi: Add domain (output form) +# +# Usage: svc_domain.cgi pkgnum{pkgnum}-svcpart{svcpart} +# http://server.name/path/svc_domain.cgi?pkgnum{pkgnum}-svcpart{svcpart} +# +# Note: Should be run setuid freeside as user nobody +# +# ivan@voicenet.com 97-jan-5 -> 97-jan-6 +# +# changes for domain template 3.5 +# ivan@voicenet.com 97-jul-24 +# +# rewrite ivan@sisd.com 98-mar-14 +# +# no GOV in instructions ivan@sisd.com 98-jul-17 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use FS::UID qw(cgisuidsetup getotaker); +use FS::Record qw(qsearch qsearchs); +use FS::svc_domain qw(fields); + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +my($action,$svcnum,$svc_domain,$pkgnum,$svcpart,$part_svc); + +if ( $QUERY_STRING =~ /^(\d+)$/ ) { #editing + + $svcnum=$1; + $svc_domain=qsearchs('svc_domain',{'svcnum'=>$svcnum}) + or die "Unknown (svc_domain) svcnum!"; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum=$cust_svc->pkgnum; + $svcpart=$cust_svc->svcpart; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $action="Edit"; + +} else { #adding + + $svc_domain=create FS::svc_domain({}); + + foreach $_ (split(/-/,$QUERY_STRING)) { + $pkgnum=$1 if /^pkgnum(\d+)$/; + $svcpart=$1 if /^svcpart(\d+)$/; + } + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $svcnum=''; + + #set fixed and default fields from part_svc + my($field); + foreach $field ( fields('svc_domain') ) { + if ( $part_svc->getfield('svc_domain__'. $field. '_flag') ne '' ) { + $svc_domain->setfield($field,$part_svc->getfield('svc_domain__'. $field) ); + } + } + + $action="Add"; + +} + +my($svc)=$part_svc->getfield('svc'); + +my($otaker)=getotaker; + +my($domain)=( + $svc_domain->domain, +); + +SendHeaders(); +print < + + $action $svc + + +
+

$action $svc

+

+
+ + + + New +
Transfer + +

Customer agrees to be bound by NSI's + +Domain Name Registration Agreement + +

Domain +
Purpose/Description: +

+
    +
  • COM is for commercial, for-profit organziations +
  • ORG is for miscellaneous, usually, non-profit organizations +
  • NET is for network infrastructure machines and organizations +
  • EDU is for 4-year, degree granting institutions + +
+US state and local government agencies, schools, libraries, museums, and individuals should register under the US domain. See RFC 1480 for a complete description of the US domain +and registration procedures. +

GOV registrations are limited to top-level US Federal Government agencies (see RFC 1816). + + + +END + -- cgit v1.2.1 -- cgit v1.2.1 From 70446cf4e4403a5984d86ef03f08881074aea101 Mon Sep 17 00:00:00 2001 From: ivan Date: Sat, 18 Jul 1998 23:39:02 +0000 Subject: Initial revision --- htdocs/misc/cancel_pkg.cgi | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100755 htdocs/misc/cancel_pkg.cgi diff --git a/htdocs/misc/cancel_pkg.cgi b/htdocs/misc/cancel_pkg.cgi new file mode 100755 index 000000000..6702a0351 --- /dev/null +++ b/htdocs/misc/cancel_pkg.cgi @@ -0,0 +1,54 @@ +#!/usr/bin/perl -Tw +# +# cancel_pkg.cgi: Cancel a package +# +# Usage: cancel_pkg.cgi pkgnum +# http://server.name/path/cancel_pkg.cgi pkgnum +# +# Note: Should be run setuid freeside as user nobody +# +# IT DOESN'T RUN THE APPROPRIATE PROGRAMS YET!!!! +# +# probably should generalize this to do cancels, suspensions, unsuspensions, etc. +# +# ivan@voicenet.com 97-jan-2 +# +# still kludgy, but now runs /dbin/cancel $pkgnum +# ivan@voicenet.com 97-feb-27 +# +# doesn't run if pkgnum doesn't match regex +# ivan@voicenet.com 97-mar-6 +# +# now redirects to enter comments +# ivan@voicenet.com 97-may-8 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-21 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::cust_pkg; +use FS::CGI qw(idiot); + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +#untaint pkgnum +$QUERY_STRING =~ /^(\d+)$/ || die "Illegal pkgnum"; +my($pkgnum)=$1; + +my($cust_pkg) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + +bless($cust_pkg,'FS::cust_pkg'); +my($error)=$cust_pkg->cancel; +idiot($error) if $error; + +$cgi->redirect("../view/cust_main.cgi?".$cust_pkg->getfield('custnum')); + -- cgit v1.2.1 -- cgit v1.2.1 From 1f363bffdb0f28d8c93ba8ca736129c149caa880 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 19 Jul 1998 00:13:49 +0000 Subject: Initial revision --- htdocs/search/cust_main.cgi | 235 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100755 htdocs/search/cust_main.cgi diff --git a/htdocs/search/cust_main.cgi b/htdocs/search/cust_main.cgi new file mode 100755 index 000000000..70ce991f7 --- /dev/null +++ b/htdocs/search/cust_main.cgi @@ -0,0 +1,235 @@ +#!/usr/bin/perl -Tw +# +# process/cust_main.cgi: Search for customers (process form) +# +# Usage: post form to: +# http://server.name/path/cust_main.cgi +# +# Note: Should be run setuid freeside as user nobody. +# +# ivan@voicenet.com 96-dec-12 +# +# rewrite ivan@sisd.com 98-mar-4 +# +# now does browsing too ivan@sisd.com 98-mar-6 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# display total, use FS::CGI ivan@sisd.com 98-jul-17 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use IO::Handle; +use IPC::Open2; +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header idiot); + +my($fuzziness)=2; #fuzziness for fuzzy searches, see man agrep + #0-4: 0=no fuzz, 4=very fuzzy (too much fuzz!) + +my($req)=new CGI::Request; +&cgisuidsetup($req->cgi); + +my(@cust_main); +my($sortby); + +my($query)=$req->cgi->var('QUERY_STRING'); +if ( $query eq 'custnum' ) { + $sortby=\*custnum_sort; + @cust_main=qsearch('cust_main',{}); +} elsif ( $query eq 'last' ) { + $sortby=\*last_sort; + @cust_main=qsearch('cust_main',{}); +} elsif ( $query eq 'company' ) { + $sortby=\*company_sort; + @cust_main=qsearch('cust_main',{}); +} else { + &cardsearch if ($req->param('card_on') ); + &lastsearch if ($req->param('last_on') ); + &companysearch if ($req->param('company_on') ); +} + +if ( scalar(@cust_main) == 1 ) { + $req->cgi->redirect("../view/cust_main.cgi?". $cust_main[0]->custnum); + exit; +} elsif ( scalar(@cust_main) == 0 ) { + idiot "No matching customers found!\n"; + exit; +} else { + + my($total)=scalar(@cust_main); + CGI::Base::SendHeaders(); # one guess + print header("Customer Search Results",''), < +

+ + + + +END + + my($lines)=16; + my($lcount)=$lines; + my(%saw,$cust_main); + foreach $cust_main ( + sort $sortby grep(!$saw{$_->custnum}++, @cust_main) + ) { + my($custnum,$last,$first,$company)=( + $cust_main->custnum, + $cust_main->getfield('last'), + $cust_main->getfield('first'), + $cust_main->company, + ); + print < + + + + +END + if ($lcount-- == 0) { # lots of little tables instead of one big one + $lcount=$lines; + print < +
Service #Domain
Cust. #Contact nameCompany
$custnum$last, $first$company
+ + + + +END + } + } + + print < + + + +END + +} + +# + +sub last_sort { + $a->getfield('last') cmp $b->getfield('last'); +} + +sub company_sort { + $a->getfield('company') cmp $b->getfield('company'); +} + +sub custnum_sort { + $a->getfield('custnum') <=> $b->getfield('custnum'); +} + +sub cardsearch { + + my($card)=$req->param('card'); + $card =~ s/\D//g; + $card =~ /^(\d{13,16})$/ or do { idiot "Illegal card number\n"; exit; }; + my($payinfo)=$1; + + push @cust_main, qsearch('cust_main',{'payinfo'=>$payinfo, 'payby'=>'CARD'}); + +} + +sub lastsearch { + my(%last_type); + foreach ( $req->param('last_type') ) { + $last_type{$_}++; + } + + $req->param('last_text') =~ /^([\w \,\.\-\']*)$/ + or do { idiot "Illegal last name"; exit; }; + my($last)=$1; + + if ( $last_type{'Exact'} + && ! $last_type{'Fuzzy'} + # && ! $last_type{'Sound-alike'} + ) { + + push @cust_main, qsearch('cust_main',{'last'=>$last}); + + } else { + + my(%last); + + my(@all_last)=map $_->getfield('last'), qsearch('cust_main',{}); + if ($last_type{'Fuzzy'}) { + my($reader,$writer) = ( new IO::Handle, new IO::Handle ); + open2($reader,$writer,'agrep',"-$fuzziness",'-i','-k', + substr($last,0,30)); + print $writer join("\n",@all_last),"\n"; + close $writer; + while (<$reader>) { + chop; + $last{$_}++; + } + close $reader; + } + + #if ($last_type{'Sound-alike'}) { + #} + + foreach ( keys %last ) { + push @cust_main, qsearch('cust_main',{'last'=>$_}); + } + + } + $sortby=\*last_sort; +} + +sub companysearch { + + my(%company_type); + foreach ( $req->param('company_type') ) { + $company_type{$_}++ + }; + + $req->param('company_text') =~ /^([\w \,\.\-\']*)$/ + or do { idiot "Illegal company"; exit; }; + my($company)=$1; + + if ( $company_type{'Exact'} + && ! $company_type{'Fuzzy'} + # && ! $company_type{'Sound-alike'} + ) { + + push @cust_main, qsearch('cust_main',{'company'=>$company}); + + } else { + + my(%company); + my(@all_company)=map $_->company, qsearch('cust_main',{}); + + if ($company_type{'Fuzzy'}) { + my($reader,$writer) = ( new IO::Handle, new IO::Handle ); + open2($reader,$writer,'agrep',"-$fuzziness",'-i','-k', + substr($company,0,30)); + print $writer join("\n",@all_company),"\n"; + close $writer; + while (<$reader>) { + chop; + $company{$_}++; + } + close $reader; + } + + #if ($company_type{'Sound-alike'}) { + #} + + foreach ( keys %company ) { + push @cust_main, qsearch('cust_main',{'company'=>$_}); + } + + } + $sortby=\*company_sort; + +} -- cgit v1.2.1 -- cgit v1.2.1 From e3e7472ec21f570c58e6bc7351019a371924c8a8 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 19 Jul 1998 00:37:57 +0000 Subject: Initial revision --- htdocs/edit/process/svc_acct_sm.cgi | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 htdocs/edit/process/svc_acct_sm.cgi diff --git a/htdocs/edit/process/svc_acct_sm.cgi b/htdocs/edit/process/svc_acct_sm.cgi new file mode 100755 index 000000000..9ad546bf4 --- /dev/null +++ b/htdocs/edit/process/svc_acct_sm.cgi @@ -0,0 +1,80 @@ +#!/usr/bin/perl -Tw +# +# process/svc_acct_sm.cgi: Add/edit a mail alias (process form) +# +# Usage: post form to: +# http://server.name/path/svc_acct_sm.cgi +# +# Note: Should br run setuid root as user nobody. +# +# lots of crufty stuff from svc_acct still in here, and modifications are (unelegantly) disabled. +# +# ivan@voicenet.com 97-jan-6 +# +# enabled modifications +# +# ivan@voicenet.com 97-may-7 +# +# fixed removal of cust_svc record on modifications! +# ivan@voicenet.com 97-jun-5 +# +# rewrite ivan@sisd.com 98-mar-15 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::svc_acct_sm; + +my($req)=new CGI::Request; # create form object +cgisuidsetup($req->cgi); + +$req->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my($svcnum)=$1; + +my($old)=qsearchs('svc_acct_sm',{'svcnum'=>$svcnum}) if $svcnum; + +#unmunge domsvc and domuid +$req->param('domsvc',(split(/:/, $req->param('domsvc') ))[0] ); +$req->param('domuid',(split(/:/, $req->param('domuid') ))[0] ); + +my($new) = create FS::svc_acct_sm ( { + map { + ($_, scalar($req->param($_))); + } qw(svcnum pkgnum svcpart domuser domuid domsvc) +} ); + +my($error); +if ( $svcnum ) { + $error = $new->replace($old); +} else { + $error = $new->insert; + $svcnum = $new->getfield('svcnum'); +} + +unless ($error) { + $req->cgi->redirect("../../view/svc_acct_sm.cgi?$svcnum"); +} else { + CGI::Base::SendHeaders(); # one guess + print < + + Error adding/editing mail alias + + +
+

Error adding/editing mail alias

+
+ Your update did not occur because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and submit the form again. + + +END + +} + -- cgit v1.2.1 -- cgit v1.2.1 From 3a60a97c305ee65b6a3c61c4b7f4bc35f8163356 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 26 Jul 1998 21:25:09 +0000 Subject: Initial revision --- htdocs/edit/svc_acct_sm.cgi | 219 ++++++++++++++++++++++++++++++++++++++++++++ htdocs/view/svc_acct_sm.cgi | 114 +++++++++++++++++++++++ 2 files changed, 333 insertions(+) create mode 100755 htdocs/edit/svc_acct_sm.cgi create mode 100755 htdocs/view/svc_acct_sm.cgi diff --git a/htdocs/edit/svc_acct_sm.cgi b/htdocs/edit/svc_acct_sm.cgi new file mode 100755 index 000000000..45a8eb8fc --- /dev/null +++ b/htdocs/edit/svc_acct_sm.cgi @@ -0,0 +1,219 @@ +#!/usr/bin/perl -Tw +# +# svc_acct_sm.cgi: Add/edit a mail alias (output form) +# +# Usage: svc_acct_sm.cgi {svcnum} | pkgnum{pkgnum}-svcpart{svcpart} +# http://server.name/path/svc_acct_sm.cgi? {svcnum} | pkgnum{pkgnum}-svcpart{svcpart} +# +# use {svcnum} for edit, pkgnum{pkgnum}-svcpart{svcpart} for add +# +# Note: Should be run setuid freeside as user nobody. +# +# should error out in a more CGI-friendly way, and should have more error checking (sigh). +# +# ivan@voicenet.com 97-jan-5 +# +# added debugging code; fixed CPU-sucking problem with trying to edit an (unaudited) mail alias (no pkgnum) +# +# ivan@voicenet.com 97-may-7 +# +# fixed uid selection +# ivan@voicenet.com 97-jun-4 +# +# uid selection across _CUSTOMER_, not just _PACKAGE_ +# +# ( i need to be rewritten with fast searches) +# +# ivan@voicenet.com 97-oct-3 +# +# added fast searches in some of the places where it is sorely needed... +# I see DBI::mysql in your future... +# ivan@voicenet.com 97-oct-23 +# +# rewrite ivan@sisd.com 98-mar-15 +# +# /var/spool/freeside/conf/domain ivan@sisd.com 98-jul-26 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::svc_acct_sm qw(fields); + +my($conf_domain)="/var/spool/freeside/conf/domain"; +open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!"; +my($mydomain)=map { + /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file + $1 +} grep $_ !~ /^(#|$)/, ; +close DOMAIN; + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +my($action,$svcnum,$svc_acct_sm,$pkgnum,$svcpart,$part_svc); +if ( $QUERY_STRING =~ /^(\d+)$/ ) { #editing + + $svcnum=$1; + $svc_acct_sm=qsearchs('svc_acct_sm',{'svcnum'=>$svcnum}) + or die "Unknown (svc_acct_sm) svcnum!"; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum=$cust_svc->pkgnum; + $svcpart=$cust_svc->svcpart; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $action="Edit"; + +} else { #adding + + $svc_acct_sm=create FS::svc_acct_sm({}); + + foreach $_ (split(/-/,$QUERY_STRING)) { #get & untaint pkgnum & svcpart + $pkgnum=$1 if /^pkgnum(\d+)$/; + $svcpart=$1 if /^svcpart(\d+)$/; + } + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $svcnum=''; + + #set fixed and default fields from part_svc + my($field); + foreach $field ( fields('svc_acct_sm') ) { + if ( $part_svc->getfield('svc_acct_sm__'. $field. '_flag') ne '' ) { + $svc_acct_sm->setfield($field,$part_svc->getfield('svc_acct_sm__'. $field) ); + } + } + + $action='Add'; + +} + +my(%username,%domain); +if ($pkgnum) { + + #find all possible uids (and usernames) + + my($u_part_svc,@u_acct_svcparts); + foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) { + push @u_acct_svcparts,$u_part_svc->getfield('svcpart'); + } + + my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + my($custnum)=$cust_pkg->getfield('custnum'); + my($i_cust_pkg); + foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) { + my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum'); + my($acct_svcpart); + foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding + #record(s) in cust_svc ( for this + #pkgnum ! ) + my($i_cust_svc); + foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) { + my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')}); + $username{$svc_acct->getfield('uid')}=$svc_acct->getfield('username'); + } + } + } + + #find all possible domains (and domsvc's) + + my($d_part_svc,@d_acct_svcparts); + foreach $d_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_domain'}) ) { + push @d_acct_svcparts,$d_part_svc->getfield('svcpart'); + } + + foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) { + my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum'); + my($acct_svcpart); + foreach $acct_svcpart (@d_acct_svcparts) { + my($i_cust_svc); + foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) { + my($svc_domain)=qsearch('svc_domain',{'svcnum'=>$i_cust_svc->getfield('svcnum')}); + $domain{$svc_domain->getfield('svcnum')}=$svc_domain->getfield('domain'); + } + } + } + +} elsif ( $action eq 'Edit' ) { + + my($svc_acct)=qsearchs('svc_acct',{'uid'=>$svc_acct_sm->domuid}); + $username{$svc_acct_sm->uid} = $svc_acct->username; + + my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$svc_acct_sm->domsvc}); + $domain{$svc_acct_sm->domsvc} = $svc_domain->domain; + +} else { + die "\$action eq Add, but \$pkgnum is null!\n"; +} + +print < + + Mail Alias $action + + +

+

Mail Alias $action

+
+
+END + +#display + + #formatting + print "
";
+
+#svcnum
+print qq!!;
+print qq!Service #!, $svcnum ? $svcnum : " (NEW)", "";
+
+#pkgnum
+print qq!!;
+ 
+#svcpart
+print qq!!;
+
+my($domuser,$domsvc,$domuid)=(
+  $svc_acct_sm->domuser,
+  $svc_acct_sm->domsvc,
+  $svc_acct_sm->domuid,
+);
+
+#domuser
+print qq!\n\nMail to  ( * for anything )!;
+
+#domsvc
+print qq! \@ ";
+
+#uid
+print qq!\nforwards to \@$mydomain mailbox.";
+
+	#formatting
+	print "
\n"; + +print qq!
!; + +print < + + +END + diff --git a/htdocs/view/svc_acct_sm.cgi b/htdocs/view/svc_acct_sm.cgi new file mode 100755 index 000000000..42623eefd --- /dev/null +++ b/htdocs/view/svc_acct_sm.cgi @@ -0,0 +1,114 @@ +#!/usr/bin/perl -Tw +# +# View svc_acct_sm records +# +# Usage: svc_acct_sm.cgi svcnum +# http://server.name/path/svc_acct_sm.cgi?svcnum +# +# Note: Should be run setuid freeside as user nobody. +# +# based on view/svc_acct.cgi +# +# ivan@voicenet.com 97-jan-5 +# +# added navigation bar +# ivan@voicenet.com 97-jan-30 +# +# rewrite ivan@sisd.com 98-mar-15 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# /var/spool/freeside/conf/domain ivan@sisd.com 98-jul-17 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); + +my($conf_domain)="/var/spool/freeside/conf/domain"; +open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!"; +my($mydomain)=map { + /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file + $1 +} grep $_ !~ /^(#|$)/, ; +close DOMAIN; + +my($cgi) = new CGI::Base; +$cgi->get; +cgisuidsetup($cgi); + +#untaint svcnum +$QUERY_STRING =~ /^(\d+)$/; +my($svcnum)=$1; +my($svc_acct_sm)=qsearchs('svc_acct_sm',{'svcnum'=>$svcnum}); +die "Unknown svcnum" unless $svc_acct_sm; + +my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}); +my($pkgnum)=$cust_svc->getfield('pkgnum'); +my($cust_pkg,$custnum); +if ($pkgnum) { + $cust_pkg=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + $custnum=$cust_pkg->getfield('custnum'); +} + +my($part_svc)=qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } ); +die "Unkonwn svcpart" unless $part_svc; + +SendHeaders(); # one guess. +print < + + Mail Alias View + + +

Mail Alias View

+END +if ($pkgnum || $custnum) { + print <View this package (#$pkgnum) | +View this customer (#$custnum) | +END +} else { + print <Cancel this (unaudited)account | +END +} + +print <Main menu
Service #$svcnum +

Edit this information + +END + +my($domsvc,$domuid,$domuser)=( + $svc_acct_sm->domsvc, + $svc_acct_sm->domuid, + $svc_acct_sm->domuser, +); +my($svc) = $part_svc->svc; +my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$domsvc}); +my($domain)=$svc_domain->domain; +my($svc_acct)=qsearchs('svc_acct',{'uid'=>$domuid}); +my($username)=$svc_acct->username; + +#formatting +print qq!


!; + +#svc +print "Service: $svc"; + +print "
"; + +print qq!Mail to !, ( ($domuser eq '*') ? "(anything)" : $domuser ) , qq!\@$domain forwards to $username\@$mydomain mailbox.!; + +print "
"; + + #formatting + print < + +END + -- cgit v1.2.1 -- cgit v1.2.1 From 6feb45e3147c6b045d68cdfaf13b3466e94b2bc5 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 26 Jul 1998 21:32:39 +0000 Subject: Initial revision --- htdocs/search/svc_acct_sm.cgi | 128 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100755 htdocs/search/svc_acct_sm.cgi diff --git a/htdocs/search/svc_acct_sm.cgi b/htdocs/search/svc_acct_sm.cgi new file mode 100755 index 000000000..3b1a4cf4e --- /dev/null +++ b/htdocs/search/svc_acct_sm.cgi @@ -0,0 +1,128 @@ +#!/usr/bin/perl -Tw +# +# svc_acct_sm.cgi: Search for domains (process form) +# +# Usage: post form to: +# http://server.name/path/svc_domain.cgi +# +# Note: Should be run setuid freeside as user nobody. +# +# ivan@voicenet.com 96-mar-5 +# +# need to look at table in results to make it more readable +# +# ivan@voicenet.com +# +# rewrite ivan@sisd.com 98-mar-15 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); + +my($conf_domain)="/var/spool/freeside/conf/domain"; +open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!"; +my($mydomain)=map { + /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file + $1 +} grep $_ !~ /^(#|$)/, ; +close DOMAIN; + +my($req)=new CGI::Request; # create form object +&cgisuidsetup($req->cgi); + +$req->param('domuser') =~ /^([a-z0-9_\-]{0,32})$/; +my($domuser)=$1; + +$req->param('domain') =~ /^([\w\-\.]+)$/ or die "Illegal domain"; +my($svc_domain)=qsearchs('svc_domain',{'domain'=>$1}) + or die "Unknown domain"; +my($domsvc)=$svc_domain->svcnum; + +my(@svc_acct_sm); +if ($domuser) { + @svc_acct_sm=qsearch('svc_acct_sm',{ + 'domuser' => $domuser, + 'domsvc' => $domsvc, + }); +} else { + @svc_acct_sm=qsearch('svc_acct_sm',{'domsvc' => $domsvc}); +} + +if ( scalar(@svc_acct_sm) == 1 ) { + my($svcnum)=$svc_acct_sm[0]->svcnum; + $req->cgi->redirect("../view/svc_acct_sm.cgi?$svcnum"); #redirect +} elsif ( scalar(@svc_acct_sm) > 1 ) { + CGI::Base::SendHeaders(); + print < + + Mail Alias Search Results + + +
+

Mail Alias Search Results

+
Cust. #Contact nameCompany +
+ + + + +END + + my($svc_acct_sm); + foreach $svc_acct_sm (@svc_acct_sm) { + my($svcnum,$domuser,$domuid,$domsvc)=( + $svc_acct_sm->svcnum, + $svc_acct_sm->domuser, + $svc_acct_sm->domuid, + $svc_acct_sm->domsvc, + ); + my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$domsvc}); + my($domain)=$svc_domain->domain; + my($svc_acct)=qsearchs('svc_acct',{'uid'=>$domuid}); + my($username)=$svc_acct->username; + my($svc_acct_svcnum)=$svc_acct->svcnum; + + print <\n \n +\n \n +END + + } + + print < + + + +END + +} else { #error + CGI::Base::SendHeaders(); # one guess + print < + + Mail Alias Search Error + + +
+

Mail Alias Search Error

+
+ Mail Alias not found. +
+ + +END + +} + -- cgit v1.2.1 -- cgit v1.2.1 From 91eaf0c699150b81a1347e02826b5fa61ef27494 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 13 Aug 1998 01:04:57 +0000 Subject: Initial revision --- htdocs/docs/CGI-modules-2.76-patch.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 htdocs/docs/CGI-modules-2.76-patch.txt diff --git a/htdocs/docs/CGI-modules-2.76-patch.txt b/htdocs/docs/CGI-modules-2.76-patch.txt new file mode 100755 index 000000000..55b50bbbe --- /dev/null +++ b/htdocs/docs/CGI-modules-2.76-patch.txt @@ -0,0 +1,23 @@ +ivan@rootwood:~/src/CGI-modules-2.76/CGI$ diff -c Base.pm Base.pm.orig +*** Base.pm Sat Jul 18 00:33:21 1998 +--- Base.pm.orig Sat Jul 18 00:06:12 1998 +*************** +*** 938,945 **** + my $orig_uri = $self->get_uri; + $self->log("Redirecting $CGI::Base::REQUEST_METHOD $orig_uri to $to_uri") + if $Debug; +! my $msg = ($perm) ? StatusHdr(301,"Moved Permanently") +! : StatusHdr(302,"Moved Temporarily"); + my $hdrs = SendHeaders($msg, LocationHdr($to_uri)); + $self->log($hdrs); + } +--- 938,945 ---- + my $orig_uri = $self->get_uri; + $self->log("Redirecting $CGI::Base::REQUEST_METHOD $orig_uri to $to_uri") + if $Debug; +! my $msg = ($perm) ? ServerHdr(301,"Moved Permanently") +! : ServerHdr(302,"Moved Temporarily"); + my $hdrs = SendHeaders($msg, LocationHdr($to_uri)); + $self->log($hdrs); + } + -- cgit v1.2.1 -- cgit v1.2.1 From 9e5d78000590c1d114bf833ed20c6c7def4a8767 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 13 Aug 1998 03:55:36 +0000 Subject: Initial revision --- Artistic | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 Artistic diff --git a/Artistic b/Artistic new file mode 100644 index 000000000..4ffc78e97 --- /dev/null +++ b/Artistic @@ -0,0 +1,125 @@ + The "Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the Package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +It also grants you the rights to reuse parts of a Package in your own +programs without transferring this License to those programs, provided +that you meet some reasonable requirements. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of this +Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) software +distribution provided that you do not advertise this Package as a +product of your own. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whomever generated +them, and may be sold commercially, and may be aggregated with this +Package. If such scripts or library files are aggregated with this +Package via the so-called "undump" or "unexec" methods of producing a +binary executable image, then distribution of such an image shall +neither be construed as a distribution of this Package nor shall it +fall under the restrictions of Paragraphs 3 and 4, provided that you do +not represent such an executable image as a Standard Version of this +Package. + +7. You may reuse parts of this Package in your own programs, provided that +you explicitly state where you got them from, in the source code (and, left +to your courtesy, in the documentation), duplicating all the associated +copyright notices and disclaimers. Besides your changes, if any, must be +clearly marked as such. Parts reused that way will no longer fall under this +license if, and only if, the name of your program(s) have no immediate +connection with the name of the Package itself or its associated programs. +You may then apply whatever restrictions you wish on the reused parts or +choose to place them in the Public Domain--this will apply only within the +context of your package. + +8. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End -- cgit v1.2.1 -- cgit v1.2.1 From c3d0d65a26090cf6d7a5bd90abc5c7d256b19d37 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 13 Aug 1998 04:15:02 +0000 Subject: Initial revision --- htdocs/docs/upgrade.html | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 htdocs/docs/upgrade.html diff --git a/htdocs/docs/upgrade.html b/htdocs/docs/upgrade.html new file mode 100644 index 000000000..d2201f601 --- /dev/null +++ b/htdocs/docs/upgrade.html @@ -0,0 +1,24 @@ + + Upgrading to 1.1.x + + +

Upgrading to 1.1.x

+
    +
  • Back up your data and current Freeside installation. +
  • Unpack a copy of the 1.0.0 distribution in a separate location. +
  • Diff your current installation against the 1.0.0 distribution. +
  • Apply all the diffs you found above, if applicable. +
  • Apply (at least) the following changes to your database: +
    +ALTER TABLE cust_main CHANGE ss ss char(11) NULL;
    +ALTER TABLE cust_main CHANGE day daytime varchar(20) NULL;
    +ALTER TABLE svc_acct CHANGE password _password varchar(25) NOT NULL;
    +ALTER TABLE part_svc CHANGE svc_acct__password svc_acct___password varchar(25) NULL;
    +ALTER TABLE part_svc CHANGE svc_acct__password_flag svc_acct___password_flag char(1) NULL;
    +ALTER TABLE agent_type CHANGE type atype varchar(80) NOT NULL;
    +
    +
  • Optionally change the field lengths and types to match a 1.1.x install; see `bin/fs-setup'. +
  • Create the necessary configuration files, +
  • Copy or symlink htdocs and site_perl to the new 1.1.x copies. +
  • Run bin/dbdef-create. This file uses MySQL-specific syntax. If you are running a different database engine you will need to modify it slightly. + -- cgit v1.2.1 -- cgit v1.2.1 From 662e0b57defc3d9a3611e804687a3d56514b6151 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 14 Aug 1998 22:11:55 +0000 Subject: Initial revision --- bin/svc_acct_sm.export | 221 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100755 bin/svc_acct_sm.export diff --git a/bin/svc_acct_sm.export b/bin/svc_acct_sm.export new file mode 100755 index 000000000..c2ec1e53f --- /dev/null +++ b/bin/svc_acct_sm.export @@ -0,0 +1,221 @@ +#!/usr/bin/perl -Tw +# +# Create and export VoiceNet_quasar.m4 +# +# ivan@voicenet.com late oct 96 +# +# change priority (after copies) to 19, not 10 +# ivan@voicenet.com 97-feb-5 +# +# put file in different place and run different script, as per matt and +# mohamed +# ivan@voicenet.com 97-mar-10 +# +# added exit if stuff is already locked ivan@voicenet.com 97-apr-15 +# +# removed mail2 +# ivan@voicenet.com 97-jul-10 +# +# rewrote lots of the bits, now exports qmail "virtualdomain", +# "recipientmap" and "rcpthosts" files as well +# +# ivan@voicenet.com 97-sep-4 +# +# adds ".extra" files +# +# ivan@voicenet.com 97-sep-29 +# +# added ".pp" files, ugh. +# +# ivan@voicenet.com 97-oct-1 +# +# rewrite ivan@sisd.com 98-mar-9 +# +# now can create .qmail-default files ivan@sisd.com 98-mar-10 +# +# put example $my_domain declaration in ivan@sisd.com 98-mar-23 +# +# /var/spool/freeside/conf and sendmail updates ivan@sisd.com 98-aug-14 + +use strict; +use Fcntl qw(:flock); +use FS::SSH qw(ssh scp); +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearch qsearchs); + +my($conf_shellm)="/var/spool/freeside/conf/shellmachine"; +my($fqmailmachines)="/var/spool/freeside/conf/qmailmachines"; +my($shellmachine); +my(@qmailmachines); +if ( -e $fqmailmachines ) { + open(SHELLMACHINE,$conf_shellm) or die "Can't open $conf_shellm: $!"; + =~ /^([\w\.\-]+)$/ or die "Illegal $conf_shellm"; + $shellmachine = $1; + close SHELLMACHINE; + open(QMAILMACHINES,$fqmailmachines); + @qmailmachines=map { + /^(.*)$/ or die "Illegal line in conf/qmailmachines"; #we trust the file + $1; + } grep $_ !~ /^(#|$)/, ; + close QMAILMACHINES; +} + +my($fsendmailmachines)="/var/spool/freeside/conf/sendmailmachines"; +my(@sendmailmachines); +if ( -e $fsendmailmachines ) { + open(SENDMAILMACHINES,$fsendmailmachines); + @sendmailmachines=map { + /^(.*)$/ or die "Illegal line in conf/sendmailmachines"; #we trust the file + $1; + } grep $_ !~ /^(#|$)/, ; + close SENDMAILMACHINES; +} + +my($conf_domain)="/var/spool/freeside/conf/domain"; +open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!"; +my($mydomain)=map { + /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file + $1 +} grep $_ !~ /^(#|$)/, ; +close DOMAIN; + +my($spooldir)="/var/spool/freeside/export"; +my($spoollock)="/var/spool/freeside/svc_acct_sm.export.lock"; + +adminsuidsetup; +umask 066; + +open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!"; +select(EXPORT); $|=1; select(STDOUT); +unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) { + seek(EXPORT,0,0); + my($pid)=; + chop($pid); + #no reason to start locks of blocking processes + die "Is another export process running under pid $pid?\n"; +} +seek(EXPORT,0,0); +print EXPORT $$,"\n"; + +my(@svc_acct_sm)=qsearch('svc_acct_sm',{}); + +( open(RCPTHOSTS,">$spooldir/rcpthosts") + and flock(RCPTHOSTS,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/rcpthosts: $!"; +( open(RECIPIENTMAP,">$spooldir/recipientmap") + and flock(RECIPIENTMAP,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/recipientmap: $!"; +( open(VIRTUALDOMAINS,">$spooldir/virtualdomains") + and flock(VIRTUALDOMAINS,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/virtualdomains: $!"; +( open(VIRTUSERTABLE,">$spooldir/virtusertable") + and flock(VIRTUSERTABLE,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/virtusertable: $!"; +( open(SENDMAIL_CW,">$spooldir/sendmail.cw") + and flock(SENDMAIL_CW,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/sendmail.cw: $!"; + +setpriority(0,0,10); + +my($svc_domain,%domain); +foreach $svc_domain ( qsearch('svc_domain',{}) ) { + my($domain)=$svc_domain->domain; + $domain{$svc_domain->svcnum}=$domain; + print RCPTHOSTS "$domain\n.$domain\n"; + print SENDMAIL_CW "$domain\n"; +} + +my(@sendmail); + +my($svc_acct_sm); +foreach $svc_acct_sm ( qsearch('svc_acct_sm') ) { + my($domsvc,$domuid,$domuser)=( + $svc_acct_sm->domsvc, + $svc_acct_sm->domuid, + $svc_acct_sm->domuser, + ); + my($domain)=$domain{$domsvc}; + my($svc_acct)=qsearchs('svc_acct',{'uid'=>$domuid}); + my($username,$dir,$uid,$gid)=( + $svc_acct->username, + $svc_acct->dir, + $svc_acct->uid, + $svc_acct->gid, + ); + next unless $username && $domain && $domuser; + + if ($domuser eq '*') { + push @sendmail, "\@$domain\t$username\n"; + print VIRTUALDOMAINS "$domain:$username-$domain\n", + ".$domain:$username-$domain\n", + ; + ### + # qmail + ssh("root\@$shellmachine", + "[ -e $dir/.qmail-default ] || { touch $dir/.qmail-default; chown $uid:$gid $dir/.qmail-default; }" + ) if ( $shellmachine && $dir && $uid ); + + } else { + print VIRTUSERTABLE "$domuser\@$domain\t$username\n"; + print RECIPIENTMAP "$domuser\@$domain:$username\@$mydomain\n"; + } + + print VIRTUSERTABLE @sendmail; + +} + +chmod 0644, "$spooldir/sendmail.cw", + "$spooldir/virtusertable", + "$spooldir/rcpthosts", + "$spooldir/recipientmap", + "$spooldir/virtualdomains", +; + +flock(SENDMAIL_CW,LOCK_UN); +flock(VIRTUSERTABLE,LOCK_UN); +flock(RCPTHOSTS,LOCK_UN); +flock(RECIPIENTMAP,LOCK_UN); +flock(VIRTUALDOMAINS,LOCK_UN); + +close SENDMAIL_CW; +close VIRTUSERTABLE; +close RCPTHOSTS; +close RECIPIENTMAP; +close VIRTUALDOMAINS; + +### +# export stuff +# + +my($sendmailmachine); +foreach $sendmailmachine (@sendmailmachines) { + scp("$spooldir/sendmail.cw","root\@$sendmailmachine:/etc/sendmail.cw.new") + == 0 or die "scp error: $!"; + scp("$spooldir/virtusertable","root\@$sendmailmachine:/etc/virtusertable.new") + == 0 or die "scp error: $!"; + ssh("root\@$sendmailmachine", + "( ". + "mv /etc/sendmail.cw.new /etc/sendmail.cw; ". + "mv /etc/virtusertable.new /etc/virtusertable; ". + #"/etc/init.d/sendmail restart; ". + " )" + ) + == 0 or die "ssh error: $!"; +} + +my($qmailmachine); +foreach $qmailmachine (@qmailmachines) { + scp("$spooldir/recipientmap","root\@$qmailmachine:/var/qmail/control/recipientmap") + == 0 or die "scp error: $!"; + scp("$spooldir/virtualdomains","root\@$qmailmachine:/var/qmail/control/virtualdomains") + == 0 or die "scp error: $!"; + scp("$spooldir/rcpthosts","root\@$qmailmachine:/var/qmail/control/rcpthosts") + == 0 or die "scp error: $!"; + #ssh("root\@$qmailmachine","/etc/init.d/qmail restart") + # == 0 or die "ssh error: $!"; +} + +unlink $spoollock; +flock(EXPORT,LOCK_UN); +close EXPORT; + -- cgit v1.2.1 -- cgit v1.2.1 From e08286e81d6b3fd588c71103138e6f3218e21bc4 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 16 Aug 1998 21:02:44 +0000 Subject: Initial revision --- bin/svc_acct.import | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100755 bin/svc_acct.import diff --git a/bin/svc_acct.import b/bin/svc_acct.import new file mode 100755 index 000000000..c4b8c5ec5 --- /dev/null +++ b/bin/svc_acct.import @@ -0,0 +1,227 @@ +#!/usr/bin/perl -Tw +# +# ivan@sisd.com 98-mar-9 +# +# changed 'password' field to '_password' because PgSQL 6.3 reserves this word +# bmccane@maxbaud.net 98-Apr-3 +# +# generalized svcparts (still needs radius import) ivan@sisd.com 98-mar-23 +# +# radius import, now an interactive script. still needs erpcd import? +# ivan@sisd.com 98-jun-24 +# +# arbitrary radius attributes ivan@sisd.com 98-aug-9 +# +# don't import /var/spool/freeside/conf/shells! ivan@sisd.com 98-aug-13 + +use strict; +use vars qw(%part_svc); +use Date::Parse; +use FS::SSH qw(iscp); +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearch); +use FS::svc_acct; + +adminsuidsetup; + +#my($spooldir)="/var/spool/freeside/export"; +my($spooldir)="unix/"; + +$FS::svc_acct::nossh_hack = 1; + +### + +%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'}); + +print "\n\n", &menu_svc, "\n", <= 2) +END +my($oisdn_svcpart)=&getpart; + +print "\n\n", &menu_svc, "\n", <svc, sort keys %part_svc ). "\n"; +} +sub getpart { + print "Enter part number, or 0 for none: "; + &getvalue; +} +sub getvalue { + my($x)=scalar(); + chop $x; + $x; +} + +print "\n\n"; + +### + +open(PASSWD,"<$spooldir/passwd.import"); +open(SHADOW,"<$spooldir/shadow.import"); +open(USERS,"<$spooldir/users.import"); + +my(%upassword,%ip,%allparam); +my(%param,$username); +while () { + chop; + next if /^$/; + if ( /^\S/ ) { + /^(\w+)\s+Password\s+=\s+"([^"]+)"(,\s+Expiration\s+=\s+"([^"]*")\s*)?$/ + or die "1Unexpected line in users.import: $_"; + my($password,$expiration); + ($username,$password,$expiration)=(lc($1),$2,$4); + $upassword{$username}=$password; + undef %param; + } else { + die "2Unexpected line in users.import: $_"; + } + while () { + chop; + if ( /^\s*$/ ) { + $ip{$username}=$param{'radius_Framed_IP_Address'}||'0e0'; + delete $param{'radius_Framed_IP_Address'}; + $allparam{$username}={ %param }; + last; + } elsif ( /^\s+([\w\-]+)\s=\s"?([\w\.\-\s]+)"?,?\s*$/ ) { + my($attribute,$value)=($1,$2); + $attribute =~ s/\-/_/g; + $param{'radius_'.$attribute}=$value; + } else { + die "3Unexpected line in users.import: $_"; + } + } +} +#? incase there isn't a terminating blank line ? +$ip{$username}=$param{'radius_Framed_IP_Address'}||'0e0'; +delete $param{'radius_Framed_IP_Address'}; +$allparam{$username}={ %param }; + +my(%password); +while () { + chop; + my($username,$password)=split(/:/); + $password{$username}=$password; +} + +while () { + chop; + my($username,$x,$uid,$gid,$finger,$dir,$shell)=split(/:/); + my($password)=$upassword{$username} || $password{$username}; + + my($maxb)=${$allparam{$username}}{'radius_Port_Limit'}; + my($svcpart); + if ( exists $upassword{$username} ) { + if ( $maxb >= 2 ) { + $svcpart = $isdn_svcpart + } elsif ( ! $maxb || $maxb == 1 ) { + $svcpart = $ppp_svcpart + } else { + die "Illegal Port-Limit in users ($username)!\n"; + } + } elsif ( $shell eq $pop_shell ) { + $svcpart = $popmail_svcpart; + } else { + $svcpart = $shell_svcpart; + } + + my($svc_acct) = create FS::svc_acct ({ + 'svcpart' => $svcpart, + 'username' => $username, + 'password' => $password, + 'uid' => $uid, + 'gid' => $gid, + 'finger' => $finger, + 'dir' => $dir, + 'shell' => $shell, + 'slipip' => $ip{$username}, + %{$allparam{$username}}, + }); + my($error); + $error=$svc_acct->insert; + die $error if $error; + + delete $allparam{$username}; + delete $upassword{$username}; +} + +#my($username); +foreach $username ( keys %upassword ) { + my($password)=$upassword{$username}; + + my($maxb)=${$allparam{$username}}{'radius_Port_Limit'} || 0; + my($svcpart); + if ( $maxb == 2 ) { + $svcpart = $oisdn_svcpart + } elsif ( ! $maxb || $maxb == 1 ) { + $svcpart = $oppp_svcpart + } else { + die "Illegal Port-Limit in users!\n"; + } + + my($svc_acct) = create FS::svc_acct ({ + 'svcpart' => $svcpart, + 'username' => $username, + 'password' => $password, + 'slipip' => $ip{$username}, + %{$allparam{$username}}, + }); + my($error); + $error=$svc_acct->insert; + die $error, if $error; + + delete $allparam{$username}; + delete $upassword{$username}; +} + -- cgit v1.2.1 -- cgit v1.2.1 From aec30c2e0ac37df10fe026d506593565a77d1c9d Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 16 Aug 1998 21:41:13 +0000 Subject: Initial revision --- htdocs/search/svc_acct.cgi | 186 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100755 htdocs/search/svc_acct.cgi diff --git a/htdocs/search/svc_acct.cgi b/htdocs/search/svc_acct.cgi new file mode 100755 index 000000000..250a741db --- /dev/null +++ b/htdocs/search/svc_acct.cgi @@ -0,0 +1,186 @@ +#!/usr/bin/perl -Tw +# +# svc_acct.cgi: Search for customers (process form) +# +# Usage: post form to: +# http://server.name/path/svc_acct.cgi +# +# Note: Should be run setuid freeside as user nobody. +# +# loosely (sp?) based on search/cust_main.cgi +# +# ivan@voicenet.com 96-jan-3 -> 96-jan-4 +# +# rewrite (now does browsing too) ivan@sisd.com 98-mar-9 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# show unlinked accounts ivan@sisd.com 98-jun-22 +# +# use FS::CGI, show total ivan@sisd.com 98-jul-17 +# +# give service and customer info too ivan@sisd.com 98-aug-16 + +use strict; +use CGI::Request; # form processing module +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header idiot); + +my($req)=new CGI::Request; # create form object +&cgisuidsetup($req->cgi); + +my(@svc_acct,$sortby); + +my($query)=$req->cgi->var('QUERY_STRING'); +#this tree is a little bit redundant +if ( $query eq 'svcnum' ) { + $sortby=\*svcnum_sort; + @svc_acct=qsearch('svc_acct',{}); +} elsif ( $query eq 'username' ) { + $sortby=\*username_sort; + @svc_acct=qsearch('svc_acct',{}); +} elsif ( $query eq 'uid' ) { + $sortby=\*uid_sort; + @svc_acct=grep $_->uid ne '', qsearch('svc_acct',{}); +} elsif ( $query eq 'UN_svcnum' ) { + $sortby=\*svcnum_sort; + @svc_acct = grep qsearchs('cust_svc',{ + 'svcnum' => $_->svcnum, + 'pkgnum' => '', + }), qsearch('svc_acct',{}); +} elsif ( $query eq 'UN_username' ) { + $sortby=\*username_sort; + @svc_acct = grep qsearchs('cust_svc',{ + 'svcnum' => $_->svcnum, + 'pkgnum' => '', + }), qsearch('svc_acct',{}); +} elsif ( $query eq 'UN_uid' ) { + $sortby=\*uid_sort; + @svc_acct = grep qsearchs('cust_svc',{ + 'svcnum' => $_->svcnum, + 'pkgnum' => '', + }), qsearch('svc_acct',{}); +} else { + &usernamesearch; +} + +if ( scalar(@svc_acct) == 1 ) { + my($svcnum)=$svc_acct[0]->svcnum; + $req->cgi->redirect("../view/svc_acct.cgi?$svcnum"); #redirect + exit; +} elsif ( scalar(@svc_acct) == 0 ) { #error + idiot("Account not found"); + exit; +} else { + my($total)=scalar(@svc_acct); + CGI::Base::SendHeaders(); # one guess + print header("Account Search Results",''), < +
+ + + + + + + + +END + + my($lines)=16; + my($lcount)=$lines; + my(%saw,$svc_acct); + foreach $svc_acct ( + sort $sortby grep(!$saw{$_->svcnum}++, @svc_acct) + ) { + my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct->svcnum }) + or die "No cust_svc record for svcnum ". $svc_acct->svcnum; + my $part_svc = qsearchs('part_svc', { 'svcpart' => $cust_svc->svcpart }) + or die "No part_svc record for svcpart ". $cust_svc->svcpart; + my($cust_pkg,$cust_main); + if ( $cust_svc->pkgnum ) { + $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc->pkgnum }) + or die "No cust_pkg record for pkgnum ". $cust_svc->pkgnum; + $cust_main = qsearchs('cust_main', { 'custnum' => $cust_pkg->custnum }) + or die "No cust_main record for custnum ". $cust_pkg->custnum; + } + my($svcnum,$username,$uid,$svc,$custnum,$last,$first,$company)=( + $svc_acct->svcnum, + $svc_acct->getfield('username'), + $svc_acct->getfield('uid'), + $part_svc->svc, + $cust_svc->pkgnum ? $cust_main->custnum : '', + $cust_svc->pkgnum ? $cust_main->getfield('last') : '', + $cust_svc->pkgnum ? $cust_main->getfield('first') : '', + $cust_svc->pkgnum ? $cust_main->company : '', + ); + my($pcustnum) = $custnum + ? "$custnum" + : "(unlinked)" + ; + my($pname) = $custnum ? "$last, $first" : ''; + print < + + + + +END + if ($lcount-- == 0) { # lots of little tables instead of one big one + $lcount=$lines; + print < +
Mail to
(click here to view mail alias)
Forwards to
(click here to view account)
+END + + print '', ( ($domuser eq '*') ? "(anything)" : $domuser ); + + print < $username\@$mydomain
Service #UsernameUIDServiceCustomer #Contact nameCompany
$svcnum$username$uid$svc + $pcustnum + $pname + $company +
+ + + + + + + + + +END + } + } + + print < + + + +END + exit; + +} + +sub svcnum_sort { + $a->getfield('svcnum') <=> $b->getfield('svcnum'); +} + +sub username_sort { + $a->getfield('username') cmp $b->getfield('username'); +} + +sub uid_sort { + $a->getfield('uid') <=> $b->getfield('uid'); +} + +sub usernamesearch { + + $req->param('username') =~ /^([\w\d\-]{2,8})$/; #untaint username_text + my($username)=$1; + + @svc_acct=qsearch('svc_acct',{'username'=>$username}); + +} + + -- cgit v1.2.1 -- cgit v1.2.1 From 09a1086ecc936117027e8fb6f015dbc038449316 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 20 Aug 1998 04:42:04 +0000 Subject: Initial revision --- eg/TEMPLATE_cust_main.import | 189 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100755 eg/TEMPLATE_cust_main.import diff --git a/eg/TEMPLATE_cust_main.import b/eg/TEMPLATE_cust_main.import new file mode 100755 index 000000000..39a5785db --- /dev/null +++ b/eg/TEMPLATE_cust_main.import @@ -0,0 +1,189 @@ +#!/usr/bin/perl -w + +# Template for importing legacy customer data +# +# ivan@sisd.com 98-aug-17 - 20 + +use strict; +use FS::UID qw(adminsuidsetup datasrc); +use FS::Record qw(fields qsearch qsearchs); +use FS::cust_main; +use FS::cust_pkg; +use Date::Parse; + +adminsuidsetup; + +# use these for the imported cust_main records (unless you have these in legacy +# data) +my($agentnum)=4; +my($refnum)=5; + +# map from legacy billing data to pkgpart, maps imported field +# LegacyBillingData to pkgpart. your names and pkgparts will be different +my(%pkgpart)=( + 'Employee' => 10, + 'Business' => 11, + 'Individual' => 12, + 'Basic PPP' => 13, + 'Slave' => 14, + 'Co-Located Server' => 15, + 'Virtual Web' => 16, + 'Perk Mail' => 17, + 'Credit Hold' => 18, +); + +my($file)="legacy_file"; + +open(CLIENT,$file) + or die "Can't open $file: $!"; + +# put a tab-separated header atop the file, or define @fields +# (use these names or change them below) +# +# for cust_main +# custnum - unique +# last - (name) +# first - (name) +# company +# address1 +# address2 +# city +# state +# zip +# country +# daytime - (phone) +# night - (phone) +# fax +# payby - CARD, BILL or COMP +# payinfo - Credit card #, P.O. # or COMP authorization +# paydate - Expiration +# tax - 'Y' for tax exempt +# for cust_pkg +# LegacyBillingData - maps via %pkgpart above to a pkgpart +# for svc_acct +# username + +my($header); +$header=; +chop $header; +my(@fields)=map { /^\s*(.*[^\s]+)\s*$/; $1 } split(/\t/,$header); +#print join("\n",@fields); + +my($error); +my($link,$line)=(0,0); +while () { + chop; + next if /^[\s\t]*$/; #skip any blank lines + + #define %svc hash for this record + my(@record)=split(/\t/); + my(%svc); + foreach (@fields) { + $svc{$_}=shift @record; + } + + # might need to massage some data like this + $svc{'payby'} =~ s/^Credit Card$/CARD/io; + $svc{'payby'} =~ s/^Check$/BILL/io; + $svc{'payby'} =~ s/^Cash$/BILL/io; + $svc{'payby'} =~ s/^$/BILL/o; + $svc{'First'} =~ s/&/and/go; + $svc{'Zip'} =~ s/\s+$//go; + + my($cust_main) = create FS::cust_main ( { + 'custnum' => $svc{'custnum'}, + 'agentnum' => $agentnum, + 'last' => $svc{'last'}, + 'first' => $svc{'first'}, + 'company' => $svc{'company'}, + 'address1' => $svc{'address1'}, + 'address2' => $svc{'address2'}, + 'city' => $svc{'city'}, + 'state' => $svc{'state'}, + 'zip' => $svc{'zip'}, + 'country' => $svc{'country'}, + 'daytime' => $svc{'daytime'}, + 'night' => $svc{'night'}, + 'fax' => $svc{'fax'}, + 'payby' => $svc{'payby'}, + 'payinfo' => $svc{'payinfo'}, + 'paydate' => $svc{'paydate'}, + 'payname' => $svc{'payname'}, + 'tax' => $svc{'tax'}, + 'refnum' => $refnum, + } ); + + $error=$cust_main->insert; + + if ( $error ) { + warn $cust_main->_dump; + warn map "$_: ". $svc{$_}. "|\n", keys %svc; + die $error; + } + + my($cust_pkg)=create FS::cust_pkg ( { + 'custnum' => $svc{'custnum'}, + 'pkgpart' => $pkgpart{$svc{'LegacyBillingData'}}, + 'setup' => '', + 'bill' => '', + 'susp' => '', + 'expire' => '', + 'cancel' => '', + } ); + + $error=$cust_pkg->insert; + if ( $error ) { + warn $svc{'LegacyBillingData'}; + die $error; + } + + unless ( $svc{'username'} ) { + warn "Empty login"; + } else { + #find svc_acct record (imported with bin/svc_acct.import) for this username + my($svc_acct)=qsearchs('svc_acct',{'username'=>$svc{'username'}}); + unless ( $svc_acct ) { + warn "username ", $svc{'username'}, " not found\n"; + } else { + #link to the cust_pkg record we created above + + #find cust_svc record for this svc_acct record + my($o_cust_svc)=qsearchs('cust_svc',{ + 'svcnum' => $svc_acct->svcnum, + 'pkgnum' => '', + } ); + unless ( $o_cust_svc ) { + warn "No unlinked cust_svc for svcnum ", $svc_acct->svcnum; + } else { + + #make sure this svcpart is in pkgpart + my($pkg_svc)=qsearchs('pkg_svc',{ + 'pkgpart' => $pkgpart{$svc{'LegacyBillingData'}}, + 'svcpart' => $o_cust_svc->svcpart, + 'quantity' => 1, + }); + unless ( $pkg_svc ) { + warn "login ", $svc{'username'}, ": No svcpart ", $o_cust_svc->svcpart, + " for pkgpart ", $pkgpart{$svc{'Acct. Type'}}, "\n" ; + } else { + + #create new cust_svc record linked to cust_pkg record + my($n_cust_svc) = create FS::cust_svc ({ + 'svcnum' => $o_cust_svc->svcnum, + 'pkgnum' => $cust_pkg->pkgnum, + 'svcpart' => $pkg_svc->svcpart, + }); + my($error) = $n_cust_svc->replace($o_cust_svc); + die $error if $error; + $link++; + } + } + } + } + + $line++; + +} + +warn "\n$link of $line lines linked\n"; + -- cgit v1.2.1 -- cgit v1.2.1 From c37abb6f18dc66836dfcafe8e3eb106747f84038 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 23 Aug 1998 23:19:48 +0000 Subject: Initial revision --- htdocs/docs/admin.html | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 htdocs/docs/admin.html diff --git a/htdocs/docs/admin.html b/htdocs/docs/admin.html new file mode 100644 index 000000000..8adddbe92 --- /dev/null +++ b/htdocs/docs/admin.html @@ -0,0 +1,6 @@ + + Administration + + +

Administration

+ -- cgit v1.2.1 -- cgit v1.2.1 From f5f9cb7c8d93868cb18a4ff00be8587ee9758af5 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 23 Aug 1998 23:39:56 +0000 Subject: Initial revision --- htdocs/docs/legacy.html | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 htdocs/docs/legacy.html diff --git a/htdocs/docs/legacy.html b/htdocs/docs/legacy.html new file mode 100644 index 000000000..40e09cb3c --- /dev/null +++ b/htdocs/docs/legacy.html @@ -0,0 +1,34 @@ + + Importing legacy data + + +

Importing legacy data

+
    +
  • bin/svc_acct.import - Import `passwd', ( `shadow' or `master.passwd' ) and RADIUS `users'. Before running bin/svc_acct.import, you need services (with table svc_acct) as follows: +
      +
    • Most accounts probably have entries in passwd and users (with Port-Limit nonexistant or 1) +
    • Some accounts have entries in passwd and users, but with Port-Limit 2 (or more) +
    • Some accounts might have entries in users only (Port-Limit 1) +
    • Some accounts might have entries in users only (Port-Limit >= 2) +
    • POP mail accounts have entries in passwd only, and have a particular shell. +
    • Everything else in passwd is a shell account. +
    +
  • bin/svc_acct_sm.import - Import qmail ( `virtualdomains' and `rcpthosts' ), or sendmail ( `virtusertable' and `sendmail.cw' ) files. Before running bin/svc_acct_sm.import, you need services as follows: +
      +
    • Domain (table svc_acct) +
    • Mail alias (table svc_acct_sm) +
    +
  • Importing customer data +
      +
    • Manually +
        +
      • Add a new customer +
      • Add one or more packages for this customer +
      • Enter a package by clicking on the package number +
      • Pick the `Link to existing' option +
      +
    • Batch - You will need to write a script to import your particular legacy data. You can use eg/TEMPLATE_cust_main.import as a starting point. +
    +
+ + -- cgit v1.2.1 -- cgit v1.2.1 From cc60ec75f2f7ddbe0dca9292126adfc53e7b4e37 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 24 Aug 1998 02:01:19 +0000 Subject: Initial revision --- bin/bill | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100755 bin/bill diff --git a/bin/bill b/bin/bill new file mode 100755 index 000000000..5c5be703d --- /dev/null +++ b/bin/bill @@ -0,0 +1,188 @@ +#!/usr/local/bin/perl -Tw +# +# bill: Bill customer(s) +# +# Usage: bill [ -c [ i ] ] [ -d 'date' ] [ -b ] +# +# Bills all customers. +# +# Adds record to /dbin/cust_bill and /dbin/cust_pay (if payment made - +# CARD & COMP), prints invoice / charges card etc. +# +# -c: Turn on collecting (you probably want this). +# +# -i: real-time billing (as opposed to batch billing). only relevant +# for credit cards. +# +# -d: Pretent it's 'date'. Date is in any format Date::Parse is happy with, +# but be careful. +# +# ## n/a ## -b: send batch when done billing +# +# ivan@voicenet.com sep/oct 96 +# +# separated billing and collections, cleaned up code. +# ivan@voicenet.com 96-nov-11 +# +# added -d option +# ivan@voicenet.com 96-nov-13 +# +# added -v option and started to implement it, added 'd:' to getopts call +# (oops!) +# ivan@voicenet.com 97-jan-2 +# +# added more debug messages, moved some searches to fssearch.pl library (for +# speed) +# rewrote "all customer" finder to know about bill dates, for speed. +# ivan@voicenet.com 97-jan-8 +# +# thought about it a while, and removed passing of the -d option to collect...? +# ivan@voicenet.com 97-jan-14 +# +# make all -v stuff STDERR +# ivan@voicenet.com 97-feb-4 +# +# added pkgnum as argument to program from /db/part_pkg, with kludge for the +# "/bin/echo XX" 's already there. +# ivan@voicenet.com 97-feb-23 +# +# - general cleanup +# - customers who are suspended can still be billed for the setup fee +# - cust_pkg record is re-read after the package setup fee program is run. +# this way, +# that program can modify the record (for example, to start accounts off +# suspended) +# (best to think four or five times before modifying anything else!) +# ivan@voicenet.com 97-feb-26 +# +# don't bill recurring fee if its not time! (was removed) +# ivan@voicenet.com 97-mar-6 +# +# added -b option, send batch when done billing. +# ivan@voicenet.com 97-apr-4 +# +#insecure dependency on line 179ish below needs to be fixed before bill is +#used setuid +# ivan@voicenet.com 97-jun-2 +# +# removed running of setup program (depriciated) +# ivan@voicenet.com 97-jul-21 +# +# rewrote for new API, removed option to specify custnums (use FS::Bill +# instead), removed -v option (?) +# ivan@voicenet.com 97-jul-22 - 23 - 25 -28 +# (need to add back in email stuff, look in /home/ivan/old/dbin/collect) +# +# s/suidsetup/adminsuidsetup/, s/FS::Search/FS::Record/, added some batch +# exporting stuff (which still needs to be generalized) and removed &idiot +# ivan@sisd.com 98-may-27 + +# setup + +use strict; +use Fcntl qw(:flock); +use Date::Parse; +use Getopt::Std; +use FS::UID qw(adminsuidsetup swapuid); +use FS::Record qw(qsearch qsearchs); +use FS::Bill; + +my($batchfile)="/var/spool/freeside/batch"; +my($batchlock)="/var/spool/freeside/batch.lock"; + +adminsuidsetup; + +&untaint_argv; #what it sounds like (eww) +use vars qw($opt_b $opt_c $opt_i $opt_d); +getopts("bcid:"); #switches + +#we're at now now (and later). +my($time)= $main::opt_d ? str2time($main::opt_d) : $^T; + +# find packages w/ bill < time && cancel != '', and create corresponding +# customer objects + +my($cust_main,%saw); +foreach $cust_main ( + map { + if ( ( $_->getfield('bill') || 0 ) <= $time && + !$saw{ $_->getfield('custnum') }++ ) { + qsearchs('cust_main',{'custnum'=> $_->getfield('custnum') } ); + } else { + (); + } + } qsearch('cust_pkg',{'cancel'=>''}) +) { + + # and bill them + + print "Billing customer #" . $cust_main->getfield('custnum') . "\n"; + + bless($cust_main,"FS::Bill"); + + my($error); + + $error=$cust_main->bill('time'=>$time); + warn "Error billing, customer #" . $cust_main->getfield('custnum') . + ":" . $error if $error; + + if ($main::opt_c) { + $error=$cust_main->collect('invoice_time'=>$time, + 'batch_card' => $main::opt_i ? 'no' : 'yes', + ); + warn "Error collecting customer #" . $cust_main->getfield('custnum') . + ":" . $error if $error; + + #sleep 1; + + } + +} + +#if ($main::opt_b) { +# +# die "Batch still waiting for reply? ($batchlock exists)\n" if -e $batchlock; +# open(BATCHLOCK,"+>>$batchlock") or die "Can't open $batchlock: $!"; +# select(BATCHLOCK); $|=1; select(STDOUT); +# unless ( flock(BATCHLOCK,,LOCK_EX|LOCK_NB) ) { +# seek(BATCHLOCK,0,0); +# my($pid)=; +# chop($pid); +# die "Is a batch running? (pid $pid)\n"; +# } +# seek(BATCHLOCK,0,0); +# print BATCHLOCK $$,"\n"; +# +# ( open(BATCH,">$batchfile") +# and flock(BATCH,LOCK_EX|LOCK_NB) +# ) or die "Can't open $batchfile: $!"; +# +# my($cust_pay_batch); +# foreach $cust_pay_batch (qsearch('cust_pay_batch',{})) { +# print BATCH join(':', +# $_->getfield('cardnum'), +# $_->getfield('exp'), +# $_->getfield('amount'), +# $_->getfield('payname') +# || $_->getfield('first'). ' '. $_->getfield('last'), +# "Description", +# $_->getfield('zip'), +# ),"\n"; +# } +# +# flock(BATCH,LOCK_UN); +# close BATCH; +# +# flock(BATCHLOCK,LOCK_UN); +# close BATCHLOCK; +#} + +# subroutines + +sub untaint_argv { + foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV + $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\""; + $ARGV[$_]=$1; + } +} + -- cgit v1.2.1 -- cgit v1.2.1 From a7ef58103f100935a0ed6a922183d6b26e0df437 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 24 Aug 1998 03:04:37 +0000 Subject: Initial revision --- fs_passwd/fs_passwd | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 fs_passwd/fs_passwd diff --git a/fs_passwd/fs_passwd b/fs_passwd/fs_passwd new file mode 100755 index 000000000..bcf09f1fe --- /dev/null +++ b/fs_passwd/fs_passwd @@ -0,0 +1,129 @@ +#!/usr/bin/perl -Tw +# +# fs_passwd +# +# portions of this script are copied from the `passwd' script in the original +# (perl 4) camel book, now archived at +# http://www.perl.com/CPAN/scripts/nutshell/ch6/passwd +# +# ivan@sisd.com 98-mar-8 +# +# password lengths 0,255 instead of 6,8 - we'll let the server process +# check the data ivan@sisd.com 98-jul-17 + +use strict; +use Getopt::Std; +use Socket; +use IO::Handle; +use vars qw($opt_f $opt_s); + +my($fs_passwdd_socket)="/usr/local/freeside/fs_passwdd_socket"; +my($freeside_uid)=scalar(getpwnam('freeside')); + +$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin'; +$ENV{'SHELL'} = '/bin/sh'; +$ENV{'IFS'} = " \t\n"; +$ENV{'CDPATH'} = ''; +$ENV{'ENV'} = ''; +$ENV{'BASH_ENV'} = ''; + +$SIG{__DIE__}= sub { system '/bin/stty', 'echo'; }; + +die "passwd program isn't running setuid to freeside\n" if $> != $freeside_uid; + +unshift @ARGV, "-f" if $0 =~ /chfn$/; +unshift @ARGV, "-s" if $0 =~ /chsh$/; + +getopts('fs'); + +my($me)=''; +if ( $_ = shift(@ARGV) ) { + /^(\w{2,8})$/; + $me = $1; +} +die "You can't change the password for $me." if $me && $<; +$me = (getpwuid($<))[0] unless $me; + +my($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell)= + getpwnam $me; + +my($old_password,$new_password,$new_gecos,$new_shell); + +if ( $opt_f || $opt_s ) { + system '/bin/stty', '-echo'; + print "Password:"; + $old_password=; + system '/bin/stty', 'echo'; + chop($old_password); + #$old_password =~ /^(.{6,8})$/ or die "\nIllegal password.\n"; + $old_password =~ /^(.{0,255})$/ or die "\nIllegal password.\n"; + $old_password = $1; + + $new_password = ''; + + if ( $opt_f ) { + print "\nChanging gecos for $me.\n"; + print "Gecos [", $gcos, "]: "; + $new_gecos=; + chop($new_gecos); + $new_gecos ||= $gcos; + $new_gecos =~ /^(.{0,255})$/ or die "\nIllegal gecos.\n"; + } else { + $new_gecos = ''; + } + + if ( $opt_s ) { + print "\nChanging shell for $me.\n"; + print "Shell [", $shell, "]: "; + $new_shell=; + chop($new_shell); + $new_shell ||= $shell; + $new_shell =~ /^(.{0,255})$/ or die "\nIllegal shell.\n"; + } else { + $new_shell = ''; + } + +} else { + + print "Changing password for $me.\n"; + print "Old password:"; + system '/bin/stty', '-echo'; + $old_password=; + chop $old_password; + #$old_password =~ /^(.{6,8})$/ or die "\nIllegal password.\n"; + $old_password =~ /^(.{0,255})$/ or die "\nIllegal password.\n"; + $old_password = $1; + print "\nEnter the new password (minimum of 6, maximum of 8 characters)\n"; + print "Please use a combination of upper and lowercase letters and numbers.\n"; + print "New password:"; + $new_password=; + chop($new_password); + #$new_password =~ /^(.{6,8})$/ or die "\nIllegal password.\n"; + $new_password =~ /^(.{0,255})$/ or die "\nIllegal password.\n"; + $new_password = $1; + print "\nRe-enter new password:"; + my($check_new_password); + $check_new_password=; + chop($check_new_password); + die "\nThey don't match; try again.\n" unless $check_new_password eq $new_password; + + $new_gecos=''; + $new_shell=''; +} +print "\n"; + +system '/bin/stty', 'echo'; + +socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; +connect(SOCK, sockaddr_un($fs_passwdd_socket)) or die "connect: $!"; +print SOCK join("\n",$me,$old_password,$new_password,$new_gecos,$new_shell),"\n"; +SOCK->flush; +my($error); +$error = ; +chop $error; + +if ($error) { + print "\nUpdate error: $error\n"; +} else { + print "\nUpdate sucessful.\n"; +} -- cgit v1.2.1 -- cgit v1.2.1 From 3875e36d98f803893af051b0f7e310bb2bb12320 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 24 Aug 1998 03:12:16 +0000 Subject: Initial revision --- fs_passwd/fs_passwd_server | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 fs_passwd/fs_passwd_server diff --git a/fs_passwd/fs_passwd_server b/fs_passwd/fs_passwd_server new file mode 100755 index 000000000..99e7c4351 --- /dev/null +++ b/fs_passwd/fs_passwd_server @@ -0,0 +1,73 @@ +#!/usr/bin/perl -Tw +# +# fs_passwd_server +# +# portions of this script are copied from the `passwd' script in the original +# (perl 4) camel book, now archived at +# http://www.perl.com/CPAN/scripts/nutshell/ch6/passwd +# +# ivan@sisd.com 98-mar-9 +# +# crypt-aware, s/password/_password/; ivan@sisd.com 98-aug-23 + +use strict; +use IO::Handle; +use FS::SSH qw(sshopen2); +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearchs); +use FS::svc_acct; + +$SIG{CHLD} = sub { wait() }; + +&adminsuidsetup; + +my($fs_passwdd)="/usr/local/sbin/fs_passwdd"; + +my($shellmachine)=shift; +die "Usage: fs_passwd_server shellmachine\n" unless $shellmachine; + +while (1) { + my($reader,$writer)=(new IO::Handle, new IO::Handle); + $writer->autoflush(1); + sshopen2($shellmachine,$reader,$writer,$fs_passwdd); + while (1) { + my($username,$old_password,$new_password,$new_gecos,$new_shell); + defined($username=<$reader>) or last; + defined($old_password=<$reader>) or last; + defined($new_password=<$reader>) or last; + defined($new_gecos=<$reader>) or last; + defined($new_shell=<$reader>) or last; + chop($username); + chop($old_password); + chop($new_password); + chop($new_gecos); + chop($new_shell); + my($svc_acct); + + #need to try both $old_password and encrypted $old_password + #maybe the crypt function in svc_acct.export needs to be a library? + my $salt = substr($old_password,0,2); + my $cold_password = crypt($old_password,$salt); + $svc_acct=qsearchs('svc_acct',{'username'=>$username, + '_password'=>$old_password, + } ) + || qsearchs('svc_acct',{'username'=>$username, + '_password'=>$cold_password, + } ); + unless ( $svc_acct ) { print $writer "Incorrect password.\n"; next; } + + my(%hash)=$svc_acct->hash; + my($new_svc_acct) = create FS::svc_acct ( \%hash ); + $new_svc_acct->setfield('_password',$new_password) + if $new_password && $new_password ne $old_password; + $new_svc_acct->setfield('finger',$new_gecos) if $new_gecos; + $new_svc_acct->setfield('shell',$new_shell) if $new_shell; + my($error)=$new_svc_acct->replace($svc_acct); + print $writer $error,"\n"; + } + close $writer; + close $reader; + sleep 60; + warn "Connection to $shellmachine lost! Reconnecting...\n"; +} + -- cgit v1.2.1 -- cgit v1.2.1 From f8e16874cdfff61799b77a69992d1204628f57cf Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 24 Aug 1998 03:28:01 +0000 Subject: Initial revision --- htdocs/docs/passwd.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 htdocs/docs/passwd.html diff --git a/htdocs/docs/passwd.html b/htdocs/docs/passwd.html new file mode 100644 index 000000000..a8f8151e2 --- /dev/null +++ b/htdocs/docs/passwd.html @@ -0,0 +1,16 @@ + + fs_passwd + + +

fs_passwd

+You may use fs_passwd/fs_passwd as a "passwd", "chfn" and "chsh" replacement on your shell machine(s) to cause password, gecos and shell changes to update your freeside machine. This can pose a security risk if not configured correctly. Do not use this feature unless you understand what you are doing! +

Currently it is assumed that the the crypt(3) function in the C library is the same on the Freeside machine as on the target machine. +
    +
  • Create a freeside account on the shell machine(s). +
  • Append the identity.pub from the freeside user on your freeside machine to the authorized_keys file of the newly created freeside user on the shell machine(s). +
  • Copy fs_passwd/fs_passwd to /usr/local/bin on the shell machine(s). (chown freeside, chmod 4755). You may link it to passwd, chfn and chsh as well. +
  • Copy fs_passwd/fs_passwdd to /usr/local/sbin on the shell machine(s). (chown freeside, chmod 500) +
  • Create /usr/local/freeside on the shell machine(s). (chown freeside, chmod 700) +
  • Run an iteration of "fs_passwd/fs_passwd_server shell.machine" as the freeside user for each shell machine (this is a daemon process). +
+ -- cgit v1.2.1 -- cgit v1.2.1 From c2f38666a7aa746c560efcf64f72261ff8a1d2f2 Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 25 Aug 1998 21:51:05 +0000 Subject: Initial revision --- htdocs/docs/export.html | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 htdocs/docs/export.html diff --git a/htdocs/docs/export.html b/htdocs/docs/export.html new file mode 100644 index 000000000..f760b97dd --- /dev/null +++ b/htdocs/docs/export.html @@ -0,0 +1,39 @@ + + File exporting + + +

File exporting

+
    +
  • bin/svc_acct.export will create UNIX `passwd', `shadow' and `master.passwd' files, ERPCD `acp_passwd' and `acp_dialup' files and a RADIUS `users' file in the `/var/spool/freeside/export' directory. Using the appropriate configuration files, you can export these files to your remote machines unattended; see below. +
      +
    • shellmachines - passwd and shadow are copied to the remote machine as /etc/passwd.new and /etc/shadow.net and then moved to /etc/passwd and /etc/shadow if no errors occur. +
    • bsdshellmachines - passwd and master.passwd are copied to the remote machine as /etc/passwd.new and /etc/master.passwd.new and moved to /etc/passwd and /etc/master.passwd if no errors occur. +
    • nismachines - passwd and shadow are copied to the `/etc/global' directory on the remote machine. If no errors occur, the command `( cd /var/yp; make; )' is executed on the remote machine. +
    • erpcdmachines - acp_passwd and acp_dialup are copied to the `/usr/annex' directory on the remote machine. If no errors occur, the command `( kill -USR1 `cat /usr/annex/erpcd.pid` )' is executed on the remote machine. +
    • radiusmachines - users is copied to the `/etc/raddb' directory on the remote machine. If no errors occur, the command `( builddbm )' is executed on the remote machine. +
    +
  • site_perl/svc_acct.pm - If a shellmachine is defined, users can be created, modified and deleted remotely; see below. +
      +
    • The command `useradd -d homedir -s shell -u uid username' is executed when a user is added. +
    • The command `userdel username' is executed with a user is deleted. +
    • If a user's home directory changes, the command `[ -d old_homedir && ( chmod u+t old_homedir; umask 022; mkdir new_homedir; cd old_homedir; find . -depth -print | cpio -pdm new_homedir; chmod u-t new_homedir; chown -R uid.gid new_homedir; rm -rf old_homedir )' is executed. +
    +
  • bin/svc_acct_sm.export will create Qmail `rcpthosts', `recipientmap' and `virtualdomains' files and Sendmail `virtusertable' and `sendmail.cw' files in the `/var/spool/freeside/export' directory. Using the appropriate configuration files, you can export these files to your remote machines unattemded; see below. +
      +
    • qmailmachines - recipientmap, virtualdomains and rcpthosts are copied to the `/var/qmail/control' directory on the remote machine. Note: If you imported qmail configuration files, run the generated `/var/spool/freeside/export/virtualdomains.FIX' on a machine with your user home directories before exporting qmail configuration files. +
    • shellmachine - The command `[ -e homedir/.qmail-default ] || { touch homedir/.qmail-default; chown uid.gid homedir/.qmail-default; }' will be run on this machine for users in the virtualdomains file. +
    • sendmailmachines - sendmail.cw and virtusertable are copied to the remote machine as /etc/sendmail.cw.new and /etc/virtusertable.new and moved to /etc/sendmail.cw and /etc/virtusertable if no errors occur. +
    +
  • site_perl/svc_acct_sm.pm - If the qmailmachines configuration file exists and a shellmachine is defined, user `.qmail-' files can be updated. +
      +
    • The command `[ -e homedir/.qmail-domain-default ] || { touch homedir/.qmail-domain-default; chown uid.gid homedir/.qmail-domain-default; }' is run. +
    +
+
Unattended remote login - Freeside can login to remote machines unattended using SSH. This can pose a security risk if not configured correctly, and will allow an intruder who breaks into your freeside machine full access to your remote machines. Do not use this feature unless you understand what you are doing! +
    +
  • As the freeside user (on your freeside machine), generate an authentication key using ssh-keygen. Since this is for unattended operation, you need to use a blank passphrase. +
  • Append the newly-created identity.pub file to root's authorized_keys on the remote machine(s). +
+ + + -- cgit v1.2.1 -- cgit v1.2.1 From 3c86cf59beb26bd3f8269a93ae1f641d88be53d1 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 3 Sep 1998 02:00:49 +0000 Subject: Initial revision --- htdocs/browse/agent.cgi | 72 +++++ htdocs/browse/agent_type.cgi | 81 ++++++ htdocs/browse/cust_main_county.cgi | 65 +++++ htdocs/browse/part_pkg.cgi | 81 ++++++ htdocs/browse/part_referral.cgi | 57 ++++ htdocs/browse/part_svc.cgi | 81 ++++++ htdocs/browse/svc_acct_pop.cgi | 63 +++++ htdocs/edit/cust_main_county-expand.cgi | 49 ++++ htdocs/edit/cust_main_county.cgi | 66 +++++ htdocs/edit/part_referral.cgi | 66 +++++ htdocs/edit/process/agent.cgi | 53 ++++ htdocs/edit/process/agent_type.cgi | 83 ++++++ htdocs/edit/process/cust_main_county-expand.cgi | 71 +++++ htdocs/edit/process/cust_main_county.cgi | 38 +++ htdocs/edit/process/part_pkg.cgi | 79 ++++++ htdocs/edit/process/part_referral.cgi | 45 ++++ htdocs/edit/process/part_svc.cgi | 47 ++++ htdocs/edit/process/svc_acct_pop.cgi | 43 +++ htdocs/edit/svc_acct_pop.cgi | 67 +++++ htdocs/view/cust_main.cgi | 336 ++++++++++++++++++++++++ 20 files changed, 1543 insertions(+) create mode 100755 htdocs/browse/agent.cgi create mode 100755 htdocs/browse/agent_type.cgi create mode 100755 htdocs/browse/cust_main_county.cgi create mode 100755 htdocs/browse/part_pkg.cgi create mode 100755 htdocs/browse/part_referral.cgi create mode 100755 htdocs/browse/part_svc.cgi create mode 100755 htdocs/browse/svc_acct_pop.cgi create mode 100755 htdocs/edit/cust_main_county-expand.cgi create mode 100755 htdocs/edit/cust_main_county.cgi create mode 100755 htdocs/edit/part_referral.cgi create mode 100755 htdocs/edit/process/agent.cgi create mode 100755 htdocs/edit/process/agent_type.cgi create mode 100755 htdocs/edit/process/cust_main_county-expand.cgi create mode 100755 htdocs/edit/process/cust_main_county.cgi create mode 100755 htdocs/edit/process/part_pkg.cgi create mode 100755 htdocs/edit/process/part_referral.cgi create mode 100755 htdocs/edit/process/part_svc.cgi create mode 100755 htdocs/edit/process/svc_acct_pop.cgi create mode 100755 htdocs/edit/svc_acct_pop.cgi create mode 100755 htdocs/view/cust_main.cgi diff --git a/htdocs/browse/agent.cgi b/htdocs/browse/agent.cgi new file mode 100755 index 000000000..cf5f2281f --- /dev/null +++ b/htdocs/browse/agent.cgi @@ -0,0 +1,72 @@ +#!/usr/bin/perl -Tw +# +# agent.cgi: browse agent +# +# ivan@sisd.com 97-dec-12 +# +# changes to allow pages to load from a relative location in the web tree. +# bmccane@maxbaud.net 98-mar-25 +# +# changed 'type' to 'atype' because type is reserved word in Pg6.3 +# bmccane@maxbaud.net 98-apr-3 +# +# agent type was linking to wrong cgi ivan@sisd.com 98-jul-18 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use FS::UID qw(cgisuidsetup swapuid); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. +print header('Agent Listing', menubar( + 'Main Menu' => '../', + 'Add new agent' => '../edit/agent.cgi' +)), < + Click on agent number to edit. +
Service #UserameUIDServiceCustomer #Contact nameCompany
+ + + + + + + +END + +my($agent); +foreach $agent ( sort { + $a->getfield('agentnum') <=> $b->getfield('agentnum') +} qsearch('agent',{}) ) { + my($hashref)=$agent->hashref; + my($typenum)=$hashref->{typenum}; + my($agent_type)=qsearchs('agent_type',{'typenum'=>$typenum}); + my($atype)=$agent_type->getfield('atype'); + print < + + + + + + +END + +} + +print < + + + +END + diff --git a/htdocs/browse/agent_type.cgi b/htdocs/browse/agent_type.cgi new file mode 100755 index 000000000..5f05bd514 --- /dev/null +++ b/htdocs/browse/agent_type.cgi @@ -0,0 +1,81 @@ +#!/usr/bin/perl -Tw +# +# agent_type.cgi: browse agent_type +# +# ivan@sisd.com 97-dec-10 +# +# Changes to allow page to work at a relative position in server +# Changes to make "Packages" display 2-wide in table (old way was too vertical) +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use FS::UID qw(cgisuidsetup swapuid); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +print header("Agent Type Listing", menubar( + 'Main Menu' => '../', + 'Add new agent type' => "../edit/agent_type.cgi", +)), <Click on agent type number to edit. +
Agent #AgentTypeFreq. (unimp.)Prog. (unimp.)
+ $hashref->{agentnum}$hashref->{agent}$atype$hashref->{freq}$hashref->{prog}
+ + + + + +END + +my($agent_type); +foreach $agent_type ( sort { + $a->getfield('typenum') <=> $b->getfield('typenum') +} qsearch('agent_type',{}) ) { + my($hashref)=$agent_type->hashref; + my(@type_pkgs)=qsearch('type_pkgs',{'typenum'=> $hashref->{typenum} }); + my($rowspan)=scalar(@type_pkgs); + $rowspan = int($rowspan/2+0.5) ; + print < + + +END + + my($type_pkgs); + my($tdcount) = -1 ; + foreach $type_pkgs ( @type_pkgs ) { + my($pkgpart)=$type_pkgs->getfield('pkgpart'); + my($part_pkg) = qsearchs('part_pkg',{'pkgpart'=> $pkgpart }); + print qq!! if ($tdcount == 0) ; + $tdcount = 0 if ($tdcount == -1) ; + print qq!"; + $tdcount ++ ; + if ($tdcount == 2) + { + print qq!\n! ; + $tdcount = 0 ; + } + } + + print ""; +} + +print <
Type #TypePackages
+ $hashref->{typenum} + $hashref->{atype}
!, + $part_pkg->getfield('pkg'),"
+ + + +END + diff --git a/htdocs/browse/cust_main_county.cgi b/htdocs/browse/cust_main_county.cgi new file mode 100755 index 000000000..d615198c9 --- /dev/null +++ b/htdocs/browse/cust_main_county.cgi @@ -0,0 +1,65 @@ +#!/usr/bin/perl -Tw +# +# cust_main_county.cgi: browse cust_main_county +# +# ivan@sisd.com 97-dec-13 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use FS::UID qw(cgisuidsetup swapuid); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. +print header("Tax Rate Listing", menubar( + 'Main Menu' => '../', + 'Edit tax rates' => "../edit/cust_main_county.cgi", +)),<Click on expand to specify tax rates by county. +

+ + + + + +END + +my($cust_main_county); +foreach $cust_main_county ( qsearch('cust_main_county',{}) ) { + my($hashref)=$cust_main_county->hashref; + print < + +END + + print ""; + + print <$hashref->{tax}% + +END + +} + +print < + + + +END + diff --git a/htdocs/browse/part_pkg.cgi b/htdocs/browse/part_pkg.cgi new file mode 100755 index 000000000..e5ff31e9e --- /dev/null +++ b/htdocs/browse/part_pkg.cgi @@ -0,0 +1,81 @@ +#!/usr/bin/perl -Tw +# +# part_svc.cgi: browse part_pkg +# +# ivan@sisd.com 97-dec-5,9 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use FS::UID qw(cgisuidsetup swapuid); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +print header("Package Part Listing",menubar( + 'Main Menu' => '../', + 'Add new package' => "../edit/part_pkg.cgi", +)), <Click on package part number to edit. +
StateCountyTax
$hashref->{state}", $hashref->{county} + ? $hashref->{county} + : qq!(ALL) !. + qq!expand! + , "
+ + + + + + + + + + +END + +my($part_pkg); +foreach $part_pkg ( sort { + $a->getfield('pkgpart') <=> $b->getfield('pkgpart') +} qsearch('part_pkg',{}) ) { + my($hashref)=$part_pkg->hashref; + my(@pkg_svc)=grep $_->getfield('quantity'), + qsearch('pkg_svc',{'pkgpart'=> $hashref->{pkgpart} }); + my($rowspan)=scalar(@pkg_svc); + print < + + + + + + +END + + my($pkg_svc); + foreach $pkg_svc ( @pkg_svc ) { + my($svcpart)=$pkg_svc->getfield('svcpart'); + my($part_svc) = qsearchs('part_svc',{'svcpart'=> $svcpart }); + print qq!\n"; + } + + print ""; +} + +print <
Part #PackageCommentSetup FeeFreq.Recur. FeeServiceQuan.
+ $hashref->{pkgpart} + $hashref->{pkg}$hashref->{comment}$hashref->{setup}$hashref->{freq}$hashref->{recur}!, + $part_svc->getfield('svc'),"", + $pkg_svc->getfield('quantity'),"
+ + + +END + diff --git a/htdocs/browse/part_referral.cgi b/htdocs/browse/part_referral.cgi new file mode 100755 index 000000000..b16fa896d --- /dev/null +++ b/htdocs/browse/part_referral.cgi @@ -0,0 +1,57 @@ +#!/usr/bin/perl -Tw +# +# part_referral.cgi: Browse part_referral +# +# ivan@sisd.com 98-feb-23 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use FS::UID qw(cgisuidsetup swapuid); +use FS::Record qw(qsearch); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. +print header("Referral Listing", menubar( + 'Main Menu' => '../', + 'Add new referral' => "../edit/part_referral.cgi", +)), <Click on referral number to edit. + + + + + +END + +my($part_referral); +foreach $part_referral ( sort { + $a->getfield('refnum') <=> $b->getfield('refnum') +} qsearch('part_referral',{}) ) { + my($hashref)=$part_referral->hashref; + print < + + + +END + +} + +print < + + + +END + diff --git a/htdocs/browse/part_svc.cgi b/htdocs/browse/part_svc.cgi new file mode 100755 index 000000000..71a556421 --- /dev/null +++ b/htdocs/browse/part_svc.cgi @@ -0,0 +1,81 @@ +#!/usr/bin/perl -Tw +# +# part_svc.cgi: browse part_svc +# +# ivan@sisd.com 97-nov-14, 97-dec-9 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use FS::UID qw(cgisuidsetup swapuid); +use FS::Record qw(qsearch); +use FS::part_svc qw(fields); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. +print header('Service Part Listing', menubar( + 'Main Menu' => '../', + 'Add new service' => "../edit/part_svc.cgi", +)),<Click on service part number to edit. +
Referral #Referral
+ $hashref->{refnum}$hashref->{referral}
+ + + + + + + + +END + +my($part_svc); +foreach $part_svc ( sort { + $a->getfield('svcpart') <=> $b->getfield('svcpart') +} qsearch('part_svc',{}) ) { + my($hashref)=$part_svc->hashref; + my($svcdb)=$hashref->{svcdb}; + my(@rows)= + grep $hashref->{${svcdb}.'__'.$_.'_flag'}, + map { /^${svcdb}__(.*)$/; $1 } + grep ! /_flag$/, + grep /^${svcdb}__/, + fields('part_svc') + ; + my($rowspan)=scalar(@rows); + print < + + + +END + my($row); + foreach $row ( @rows ) { + my($flag)=$part_svc->getfield($svcdb.'__'.$row.'_flag'); + print ""; + } +print ""; +} + +print < + + + +END + diff --git a/htdocs/browse/svc_acct_pop.cgi b/htdocs/browse/svc_acct_pop.cgi new file mode 100755 index 000000000..a8a3a9224 --- /dev/null +++ b/htdocs/browse/svc_acct_pop.cgi @@ -0,0 +1,63 @@ +#!/usr/bin/perl -Tw +# +# svc_acct_pop.cgi: browse pops +# +# ivan@sisd.com 98-mar-8 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use FS::UID qw(cgisuidsetup swapuid); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. +print header('POP Listing', menubar( + 'Main Menu' => '../', + 'Add new POP' => "../edit/svc_acct_pop.cgi", +)), <Click on pop number to edit. +
Part #ServiceTableFieldActionValue
+ $hashref->{svcpart} + $hashref->{svc}$hashref->{svcdb}$row"; + if ( $flag eq "D" ) { print "Default"; } + elsif ( $flag eq "F" ) { print "Fixed"; } + else { print "(Unknown!)"; } + print "",$part_svc->getfield($svcdb."__".$row),"
+ + + + + + + +END + +my($svc_acct_pop); +foreach $svc_acct_pop ( sort { + $a->getfield('popnum') <=> $b->getfield('popnum') +} qsearch('svc_acct_pop',{}) ) { + my($hashref)=$svc_acct_pop->hashref; + print < + + + + + + +END + +} + +print < + + + +END + diff --git a/htdocs/edit/cust_main_county-expand.cgi b/htdocs/edit/cust_main_county-expand.cgi new file mode 100755 index 000000000..59ff7043a --- /dev/null +++ b/htdocs/edit/cust_main_county-expand.cgi @@ -0,0 +1,49 @@ +#!/usr/bin/perl -Tw +# +# cust_main_county-expand.cgi: Expand a state into counties (output form) +# +# ivan@sisd.com 97-dec-16 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +$cgi->var('QUERY_STRING') =~ /^(\d+)$/ + or die "Illegal taxnum!"; +my($taxnum)=$1; + +my($cust_main_county)=qsearchs('cust_main_county',{'taxnum'=>$taxnum}); +die "Can't expand entry!" if $cust_main_county->getfield('county'); + +print header("Tax Rate (expand state)", menubar( + 'Main Menu' => '../', +)), < + + Separate counties by + line + (rumor has it broken on some browsers) or + whitespace. +
+
+ + + + +END + diff --git a/htdocs/edit/cust_main_county.cgi b/htdocs/edit/cust_main_county.cgi new file mode 100755 index 000000000..904d58346 --- /dev/null +++ b/htdocs/edit/cust_main_county.cgi @@ -0,0 +1,66 @@ +#!/usr/bin/perl -Tw +# +# cust_main_county.cgi: Edit tax rates (output form) +# +# ivan@sisd.com 97-dec-13-16 +# +# Changes to allow page to work at a relative position in server +# Changed tax field to accept 6 chars (MO uses 6.1%) +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +print header("Edit tax rates", menubar( + 'Main Menu' => '../', +)),< +
POP #CityStateArea codeExchange
+ $hashref->{popnum}$hashref->{city}$hashref->{state}$hashref->{ac}$hashref->{exch}
+ + + + + +END + +my($cust_main_county); +foreach $cust_main_county ( qsearch('cust_main_county',{}) ) { + my($hashref)=$cust_main_county->hashref; + print < + +END + + print ""; + + print qq!!; +END + +} + +print < + + + + + +END + diff --git a/htdocs/edit/part_referral.cgi b/htdocs/edit/part_referral.cgi new file mode 100755 index 000000000..f29802239 --- /dev/null +++ b/htdocs/edit/part_referral.cgi @@ -0,0 +1,66 @@ +#!/usr/bin/perl -Tw +# +# agent.cgi: Add/Edit referral (output form) +# +# ivan@sisd.com 98-feb-23 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# confisuing typo on submit button ivan@sisd.com 98-jun-14 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::part_referral; +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +my($part_referral,$action); +if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing + $part_referral=qsearchs('part_referral',{'refnum'=>$1}); + $action='Edit'; +} else { #adding + $part_referral=create FS::part_referral {}; + $action='Add'; +} +my($hashref)=$part_referral->hashref; + +print header("$action Referral", menubar( + 'Main Menu' => '../', + 'View all referrals' => "../browse/part_referral.cgi", +)), < +END + +#display + +print qq!!, + "Referral #", $hashref->{refnum} ? $hashref->{refnum} : "(NEW)"; + +print < +Referral + +END + +print qq!
!; + +print < + + +END + diff --git a/htdocs/edit/process/agent.cgi b/htdocs/edit/process/agent.cgi new file mode 100755 index 000000000..5d1ce3232 --- /dev/null +++ b/htdocs/edit/process/agent.cgi @@ -0,0 +1,53 @@ +#!/usr/bin/perl -Tw +# +# process/agent.cgi: Edit agent (process form) +# +# ivan@sisd.com 97-dec-12 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::agent qw(fields); +use FS::CGI qw(idiot); + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +my($agentnum)=$req->param('agentnum'); + +my($old)=qsearchs('agent',{'agentnum'=>$agentnum}) if $agentnum; + +#unmunge typenum +$req->param('typenum') =~ /^(\d+)(:.*)?$/; +$req->param('typenum',$1); + +my($new)=create FS::agent ( { + map { + $_, $req->param($_); + } fields('agent') +} ); + +my($error); +if ( $agentnum ) { + $error=$new->replace($old); +} else { + $error=$new->insert; + $agentnum=$new->getfield('agentnum'); +} + +if ( $error ) { + &idiot($error); +} else { + #$req->cgi->redirect("../../view/agent.cgi?$agentnum"); + #$req->cgi->redirect("../../edit/agent.cgi?$agentnum"); + $req->cgi->redirect("../../browse/agent.cgi"); +} + diff --git a/htdocs/edit/process/agent_type.cgi b/htdocs/edit/process/agent_type.cgi new file mode 100755 index 000000000..43f129fd5 --- /dev/null +++ b/htdocs/edit/process/agent_type.cgi @@ -0,0 +1,83 @@ +#!/usr/bin/perl -Tw +# +# process/agent_type.cgi: Edit agent type (process form) +# +# ivan@sisd.com 97-dec-11 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::agent_type qw(fields); +use FS::type_pkgs; +use FS::CGI qw(idiot); + +my($req)=new CGI::Request; +&cgisuidsetup($req->cgi); + +my($typenum)=$req->param('typenum'); +my($old)=qsearchs('agent_type',{'typenum'=>$typenum}) if $typenum; + +my($new)=create FS::agent_type ( { + map { + $_, $req->param($_); + } fields('agent_type') +} ); + +my($error); +if ( $typenum ) { + $error=$new->replace($old); +} else { + $error=$new->insert; + $typenum=$new->getfield('typenum'); +} + +if ( $error ) { + idiot($error); + exit; +} + +my($part_pkg); +foreach $part_pkg (qsearch('part_pkg',{})) { + my($pkgpart)=$part_pkg->getfield('pkgpart'); + + my($type_pkgs)=qsearchs('type_pkgs',{ + 'typenum' => $typenum, + 'pkgpart' => $pkgpart, + }); + if ( $type_pkgs && ! $req->param("pkgpart$pkgpart") ) { + my($d_type_pkgs)=$type_pkgs; #need to save $type_pkgs for below. + $error=$d_type_pkgs->del; #FS::Record not FS::type_pkgs, + #so ->del not ->delete. hmm. hmm. + if ( $error ) { + idiot($error); + exit; + } + + } elsif ( $req->param("pkgpart$pkgpart") + && ! $type_pkgs + ) { + #ok to clobber it now (but bad form nonetheless?) + $type_pkgs=create FS::type_pkgs ({ + 'typenum' => $typenum, + 'pkgpart' => $pkgpart, + }); + $error= $type_pkgs->insert; + if ( $error ) { + idiot($error); + exit; + } + } + +} + +#$req->cgi->redirect("../../view/agent_type.cgi?$typenum"); +#$req->cgi->redirect("../../edit/agent_type.cgi?$typenum"); +$req->cgi->redirect("../../browse/agent_type.cgi"); + diff --git a/htdocs/edit/process/cust_main_county-expand.cgi b/htdocs/edit/process/cust_main_county-expand.cgi new file mode 100755 index 000000000..a821560c6 --- /dev/null +++ b/htdocs/edit/process/cust_main_county-expand.cgi @@ -0,0 +1,71 @@ +#!/usr/bin/perl -Tw +# +# process/cust_main_county-expand.cgi: Expand counties (process form) +# +# ivan@sisd.com 97-dec-16 +# +# Changes to allow page to work at a relative position in server +# Added import of datasrc from UID.pm for Pg6.3 +# Default tax to 0.0 if using Pg6.3 +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI +# undo default tax to 0.0 if using Pg6.3: comes from pre-expanded record +# for that state +#ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup datasrc); +use FS::Record qw(qsearch qsearchs); +use FS::cust_main_county; +use FS::CGI qw(eidiot); + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +$req->param('taxnum') =~ /^(\d+)$/ or die "Illegal taxnum!"; +my($taxnum)=$1; +my($cust_main_county)=qsearchs('cust_main_county',{'taxnum'=>$taxnum}) + or die ("Unknown taxnum!"); + +my(@counties); +if ( $req->param('delim') eq 'n' ) { + @counties=split(/\n/,$req->param('counties')); +} elsif ( $req->param('delim') eq 's' ) { + @counties=split(/\s+/,$req->param('counties')); +} else { + die "Illegal delim!"; +} + +@counties=map { + /^\s*([\w\- ]+)\s*$/ or eidiot("Illegal county"); + $1; +} @counties; + +my($county); +foreach ( @counties) { + my(%hash)=$cust_main_county->hash; + my($new)=create FS::cust_main_county \%hash; + $new->setfield('taxnum',''); + $new->setfield('county',$_); + #if (datasrc =~ m/Pg/) + #{ + # $new->setfield('tax',0.0); + #} + my($error)=$new->insert; + die $error if $error; +} + +unless ( qsearch('cust_main',{ + 'state' => $cust_main_county->getfield('state'), + 'county' => $cust_main_county->getfield('county'), +} ) ) { + my($error)=($cust_main_county->delete); + die $error if $error; +} + +$req->cgi->redirect("../../edit/cust_main_county.cgi"); + diff --git a/htdocs/edit/process/cust_main_county.cgi b/htdocs/edit/process/cust_main_county.cgi new file mode 100755 index 000000000..58eaa63ce --- /dev/null +++ b/htdocs/edit/process/cust_main_county.cgi @@ -0,0 +1,38 @@ +#!/usr/bin/perl -Tw +# +# process/agent.cgi: Edit cust_main_county (process form) +# +# ivan@sisd.com 97-dec-16 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::cust_main_county; +use FS::CGI qw(eidiot); + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +foreach ( $req->params ) { + /^tax(\d+)$/ or die "Illegal form $_!"; + my($taxnum)=$1; + my($old)=qsearchs('cust_main_county',{'taxnum'=>$taxnum}) + or die "Couldn't find taxnum $taxnum!"; + next unless $old->getfield('tax') ne $req->param("tax$taxnum"); + my(%hash)=$old->hash; + $hash{tax}=$req->param("tax$taxnum"); + my($new)=create FS::cust_main_county \%hash; + my($error)=$new->replace($old); + eidiot($error) if $error; +} + +$req->cgi->redirect("../../browse/cust_main_county.cgi"); + diff --git a/htdocs/edit/process/part_pkg.cgi b/htdocs/edit/process/part_pkg.cgi new file mode 100755 index 000000000..7d787819a --- /dev/null +++ b/htdocs/edit/process/part_pkg.cgi @@ -0,0 +1,79 @@ +#!/usr/bin/perl -Tw +# +# process/part_pkg.cgi: Edit package definitions (process form) +# +# ivan@sisd.com 97-dec-10 +# +# don't update non-changing records in part_svc (causing harmless but annoying +# "Records identical" errors). ivan@sisd.com 98-feb-19 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# Added `|| 0 ' when getting quantity off web page ivan@sisd.com 98-jun-4 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::part_pkg qw(fields); +use FS::pkg_svc; +use FS::CGI qw(eidiot); + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +my($pkgpart)=$req->param('pkgpart'); + +my($old)=qsearchs('part_pkg',{'pkgpart'=>$pkgpart}) if $pkgpart; + +my($new)=create FS::part_pkg ( { + map { + $_, $req->param($_); + } fields('part_pkg') +} ); + +if ( $pkgpart ) { + my($error)=$new->replace($old); + eidiot($error) if $error; +} else { + my($error)=$new->insert; + eidiot($error) if $error; + $pkgpart=$new->getfield('pkgpart'); +} + +my($part_svc); +foreach $part_svc (qsearch('part_svc',{})) { +# don't update non-changing records in part_svc (causing harmless but annoying +# "Records identical" errors). ivan@sisd.com 98-jan-19 + #my($quantity)=$req->param('pkg_svc'. $part_svc->getfield('svcpart')), + my($quantity)=$req->param('pkg_svc'. $part_svc->svcpart) || 0, + my($old_pkg_svc)=qsearchs('pkg_svc',{ + 'pkgpart' => $pkgpart, + 'svcpart' => $part_svc->getfield('svcpart'), + }); + my($old_quantity)=$old_pkg_svc ? $old_pkg_svc->quantity : 0; + next unless $old_quantity != $quantity; #!here + my($new_pkg_svc)=create FS::pkg_svc({ + 'pkgpart' => $pkgpart, + 'svcpart' => $part_svc->getfield('svcpart'), + #'quantity' => $req->param('pkg_svc'. $part_svc->getfield('svcpart')), + 'quantity' => $quantity, + }); + if ($old_pkg_svc) { + my($error)=$new_pkg_svc->replace($old_pkg_svc); + eidiot($error) if $error; + } else { + my($error)=$new_pkg_svc->insert; + eidiot($error) if $error; + } +} + +#$req->cgi->redirect("../../view/part_pkg.cgi?$pkgpart"); +#$req->cgi->redirect("../../edit/part_pkg.cgi?$pkgpart"); +$req->cgi->redirect("../../browse/part_pkg.cgi"); + diff --git a/htdocs/edit/process/part_referral.cgi b/htdocs/edit/process/part_referral.cgi new file mode 100755 index 000000000..08a4c01d0 --- /dev/null +++ b/htdocs/edit/process/part_referral.cgi @@ -0,0 +1,45 @@ +#!/usr/bin/perl -Tw +# +# process/part_referral.cgi: Edit referrals (process form) +# +# ivan@sisd.com 98-feb-23 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::part_referral qw(fields); +use FS::CGI qw(eidiot); +use FS::CGI qw(eidiot); + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +my($refnum)=$req->param('refnum'); + +my($new)=create FS::part_referral ( { + map { + $_, $req->param($_); + } fields('part_referral') +} ); + +if ( $refnum ) { + my($old)=qsearchs('part_referral',{'refnum'=>$refnum}); + eidiot("(Old) Record not found!") unless $old; + my($error)=$new->replace($old); + eidiot($error) if $error; +} else { + my($error)=$new->insert; + eidiot($error) if $error; +} + +$refnum=$new->getfield('refnum'); +$req->cgi->redirect("../../browse/part_referral.cgi"); + diff --git a/htdocs/edit/process/part_svc.cgi b/htdocs/edit/process/part_svc.cgi new file mode 100755 index 000000000..0f0fbc6e8 --- /dev/null +++ b/htdocs/edit/process/part_svc.cgi @@ -0,0 +1,47 @@ +#!/usr/bin/perl -Tw +# +# process/part_svc.cgi: Edit service definitions (process form) +# +# ivan@sisd.com 97-nov-14 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs); +use FS::part_svc qw(fields); +use FS::CGI qw(eidiot); + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +my($svcpart)=$req->param('svcpart'); + +my($old)=qsearchs('part_svc',{'svcpart'=>$svcpart}) if $svcpart; + +my($new)=create FS::part_svc ( { + map { + $_, $req->param($_); +# } qw(svcpart svc svcdb) + } fields('part_svc') +} ); + +if ( $svcpart ) { + my($error)=$new->replace($old); + eidiot($error) if $error; +} else { + my($error)=$new->insert; + eidiot($error) if $error; + $svcpart=$new->getfield('svcpart'); +} + +#$req->cgi->redirect("../../view/part_svc.cgi?$svcpart"); +#$req->cgi->redirect("../../edit/part_svc.cgi?$svcpart"); +$req->cgi->redirect("../../browse/part_svc.cgi"); + diff --git a/htdocs/edit/process/svc_acct_pop.cgi b/htdocs/edit/process/svc_acct_pop.cgi new file mode 100755 index 000000000..18d7940b4 --- /dev/null +++ b/htdocs/edit/process/svc_acct_pop.cgi @@ -0,0 +1,43 @@ +#!/usr/bin/perl -Tw +# +# process/svc_acct_pop.cgi: Edit POP (process form) +# +# ivan@sisd.com 98-mar-8 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Request; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::svc_acct_pop qw(fields); +use FS::CGI qw(eidiot); + +my($req)=new CGI::Request; # create form object + +&cgisuidsetup($req->cgi); + +my($popnum)=$req->param('popnum'); + +my($old)=qsearchs('svc_acct_pop',{'popnum'=>$popnum}) if $popnum; + +my($new)=create FS::svc_acct_pop ( { + map { + $_, $req->param($_); + } fields('svc_acct_pop') +} ); + +if ( $popnum ) { + my($error)=$new->replace($old); + eidiot($error) if $error; +} else { + my($error)=$new->insert; + eidiot($error) if $error; + $popnum=$new->getfield('popnum'); +} +$req->cgi->redirect("../../browse/svc_acct_pop.cgi"); + diff --git a/htdocs/edit/svc_acct_pop.cgi b/htdocs/edit/svc_acct_pop.cgi new file mode 100755 index 000000000..46d803f07 --- /dev/null +++ b/htdocs/edit/svc_acct_pop.cgi @@ -0,0 +1,67 @@ +#!/usr/bin/perl -Tw +# +# svc_acct_pop.cgi: Add/Edit pop (output form) +# +# ivan@sisd.com 98-mar-8 +# +# Changes to allow page to work at a relative position in server +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::svc_acct_pop; +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; + +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. + +my($svc_acct_pop,$action); +if ( $cgi->var('QUERY_STRING') =~ /^(\d+)$/ ) { #editing + $svc_acct_pop=qsearchs('svc_acct_pop',{'popnum'=>$1}); + $action='Edit'; +} else { #adding + $svc_acct_pop=create FS::svc_acct_pop {}; + $action='Add'; +} +my($hashref)=$svc_acct_pop->hashref; + +print header("$action POP", menubar( + 'Main Menu' => '../', + 'View all POPs' => "../browse/svc_acct_pop.cgi", +)), < +END + +#display + +print qq!!, + "POP #", $hashref->{popnum} ? $hashref->{popnum} : "(NEW)"; + +print < +City +State +Area Code +Exchange + +END + +print qq!
!; + +print < + + +END + diff --git a/htdocs/view/cust_main.cgi b/htdocs/view/cust_main.cgi new file mode 100755 index 000000000..ca5fcd94f --- /dev/null +++ b/htdocs/view/cust_main.cgi @@ -0,0 +1,336 @@ +#!/usr/bin/perl -Tw +# +# cust_main.cgi: View a customer +# +# Usage: cust_main.cgi custnum +# http://server.name/path/cust_main.cgi?custnum +# +# Note: Should be run setuid freeside as user nobody. +# +# the payment history section could use some work, see below +# +# ivan@voicenet.com 96-nov-29 -> 96-dec-11 +# +# added navigation bar (go to main menu ;) +# ivan@voicenet.com 97-jan-30 +# +# changes to the way credits/payments are applied (the links are here). +# ivan@voicenet.com 97-apr-21 +# +# added debugging code to diagnose CPU sucking problem. +# ivan@voicenet.com 97-may-19 +# +# CPU sucking problem was in comment code? fixed? +# ivan@voicenet.com 97-may-22 +# +# rewrote for new API +# ivan@voicenet.com 97-jul-22 +# +# Changes to allow page to work at a relative position in server +# Changed 'day' to 'daytime' because Pg6.3 reserves the day word +# bmccane@maxbaud.net 98-apr-3 +# +# lose background, FS::CGI ivan@sisd.com 98-sep-2 + +use strict; +use CGI::Base qw(:DEFAULT :CGI); # CGI module +use CGI::Carp qw(fatalsToBrowser); +use Date::Format; +use FS::UID qw(cgisuidsetup); +use FS::Record qw(qsearchs qsearch); +use FS::CGI qw(header menubar); + +my($cgi) = new CGI::Base; +$cgi->get; +&cgisuidsetup($cgi); + +SendHeaders(); # one guess. +print header("Customer View", menubar( + 'Main Menu' => '../', +)),< +END + +#untaint custnum & get customer record +$QUERY_STRING =~ /^(\d+)$/; +my($custnum)=$1; +my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum}); +die "Customer not found!" unless $cust_main; +my($hashref)=$cust_main->hashref; + +#custnum +print "
Customer #$custnum
", + qq!
Customer Information | !, + qq!Comments | !, + qq!Packages | !, + qq!Payment History
!; + +#bill now linke +print qq!
!, + qq!Bill this customer now
!; + +#formatting +print qq!
Customer Information!, + qq!!, + qq!
Edit this information
!; + +#agentnum +my($agent)=qsearchs('agent',{ + 'agentnum' => $cust_main->getfield('agentnum') +} ); +die "Agent not found!" unless $agent; +print "
Agent #" , $agent->getfield('agentnum') , ": " , + $agent->getfield('agent') , ""; + +#refnum +my($referral)=qsearchs('part_referral',{'refnum' => $cust_main->refnum}); +die "Referral not found!" unless $referral; +print "
Referral #", $referral->refnum, ": ", + $referral->referral, "<\B>"; + +#last, first +print "

", $hashref->{'last'}, ", ", $hashref->{first}, ""; + +#ss +print " (SS# ", $hashref->{ss}, ")" if $hashref->{ss}; + +#company +print "
", $hashref->{company}, "" if $hashref->{company}; + +#address1 +print "
", $hashref->{address1}, ""; + +#address2 +print "
", $hashref->{address2}, "" if $hashref->{address2}; + +#city +print "
", $hashref->{city}, ""; + +#county +print " (", $hashref->{county}, " county)" if $hashref->{county}; + +#state +print ",", $hashref->{state}, ""; + +#zip +print " ", $hashref->{zip}, ""; + +#country +print "
", $hashref->{country}, "" + unless $hashref->{country} eq "US"; + +#daytime +print "

", $hashref->{daytime}, "" if $hashref->{daytime}; +print " (Day)" if $hashref->{daytime} && $hashref->{night}; + +#night +print "
", $hashref->{night}, "" if $hashref->{night}; +print " (Night)" if $hashref->{daytime} && $hashref->{night}; + +#fax +print "
", $hashref->{fax}, " (Fax)" if $hashref->{fax}; + +#payby/payinfo/paydate/payname +if ($hashref->{payby} eq "CARD") { + print "

Card #", $hashref->{payinfo}, " Exp. ", + $hashref->{paydate}, ""; + print " (", $hashref->{payname}, ")" if $hashref->{payname}; +} elsif ($hashref->{payby} eq "BILL") { + print "

Bill"; + print " on P.O. #", $hashref->{payinfo}, "" + if $hashref->{payinfo}; + print " until ", $hashref->{paydate}, "" + if $hashref->{paydate}; + print " to ", $hashref->{payname}, " at above address" + if $hashref->{payname}; +} elsif ($hashref->{payby} eq "COMP") { + print "

Access complimentary"; + print " courtesy of ", $hashref->{payinfo}, "" + if $hashref->{payinfo}; + print " until ", $hashref->{paydate}, "" + if $hashref->{paydate}; +} else { + print "Unknown payment type ", $hashref->{payby}, "!"; +} + +#tax +print "
(Tax exempt)" if $hashref->{tax}; + +#otaker +print "

Order taken by ", $hashref->{otaker}, ""; + +#formatting +print qq!


Packages!, + qq!
Click on package number to view/edit package.!, + qq!
Add/Edit packages!, + qq!

!; + +#display packages + +#formatting +print qq!
StateCountyTax
$hashref->{state}", $hashref->{county} + ? $hashref->{county} + : '(ALL)' + , "%
\n!, + qq!\n!, + qq!!, + qq!!, + qq!\n!; + +#get package info +my(@packages)=qsearch('cust_pkg',{'custnum'=>$custnum}); +my($package); +foreach $package (@packages) { + my($pref)=$package->hashref; + my($part_pkg)=qsearchs('part_pkg',{ + 'pkgpart' => $pref->{pkgpart} + } ); + print qq!!, + "", + "", + "", + "", + "", + "", + ""; +} + +#formatting +print "
#Package!, + qq!Dates
Setup!, + qq!Next bill!, + qq!Susp.Expire!, + qq!Cancel
!, + $pref->{pkgnum}, qq!", $part_pkg->getfield('pkg'), " - ", + $part_pkg->getfield('comment'), "", + $pref->{setup} ? time2str("%D",$pref->{setup} ) : "" , + "", + $pref->{bill} ? time2str("%D",$pref->{bill} ) : "" , + "", + $pref->{susp} ? time2str("%D",$pref->{susp} ) : "" , + "", + $pref->{expire} ? time2str("%D",$pref->{expire} ) : "" , + "", + $pref->{cancel} ? time2str("%D",$pref->{cancel} ) : "" , + "
"; + +#formatting +print qq!


Payment History!, + qq!
!, + qq!Click on invoice to view invoice/enter payment.
!, + qq!!, + qq!Post Credit / Refund

!; + +#get payment history +# +# major problem: this whole thing is way too sloppy. +# minor problem: the description lines need better formatting. + +my(@history); + +my(@bills)=qsearch('cust_bill',{'custnum'=>$custnum}); +my($bill); +foreach $bill (@bills) { + my($bref)=$bill->hashref; + push @history, + $bref->{_date} . qq!\tInvoice #! . $bref->{invnum} . + qq! (Balance \$! . $bref->{owed} . qq!)\t! . + $bref->{charged} . qq!\t\t\t!; + + my(@payments)=qsearch('cust_pay',{'invnum'=> $bref->{invnum} } ); + my($payment); + foreach $payment (@payments) { +# my($pref)=$payment->hashref; + my($date,$invnum,$payby,$payinfo,$paid)=($payment->getfield('_date'), + $payment->getfield('invnum'), + $payment->getfield('payby'), + $payment->getfield('payinfo'), + $payment->getfield('paid'), + ); + push @history, + "$date\tPayment, Invoice #$invnum ($payby $payinfo)\t\t$paid\t\t"; + } +} + +my(@credits)=qsearch('cust_credit',{'custnum'=>$custnum}); +my($credit); +foreach $credit (@credits) { + my($cref)=$credit->hashref; + push @history, + $cref->{_date} . "\tCredit #" . $cref->{crednum} . ", (Balance \$" . + $cref->{credited} . ") by " . $cref->{otaker} . " - " . + $cref->{reason} . "\t\t\t" . $cref->{amount} . "\t"; + + my(@refunds)=qsearch('cust_refund',{'crednum'=> $cref->{crednum} } ); + my($refund); + foreach $refund (@refunds) { + my($rref)=$refund->hashref; + push @history, + $rref->{_date} . "\tRefund, Credit #" . $rref->{crednum} . " (" . + $rref->{payby} . " " . $rref->{payinfo} . ") by " . + $rref->{otaker} . " - ". $rref->{reason} . "\t\t\t\t" . + $rref->{refund}; + } +} + + #formatting + print < + + + + + + + + + +END + +#display payment history + +my($balance)=0; +my($item); +foreach $item (sort keyfield_numerically @history) { + my($date,$desc,$charge,$payment,$credit,$refund)=split(/\t/,$item); + $charge ||= 0; + $payment ||= 0; + $credit ||= 0; + $refund ||= 0; + $balance += $charge - $payment; + $balance -= $credit - $refund; + + print "", + "", + "", + "", + "", + "", + "", + "\n"; +} + +#formatting +print "
DateDescriptionChargePaymentIn-house
Credit
RefundBalance
",time2str("%D",$date),"$desc", + ( $charge ? "\$".sprintf("%.2f",$charge) : '' ), + "", + ( $payment ? "- \$".sprintf("%.2f",$payment) : '' ), + "", + ( $credit ? "- \$".sprintf("%.2f",$credit) : '' ), + "", + ( $refund ? "\$".sprintf("%.2f",$refund) : '' ), + "\$" . sprintf("%.2f",$balance), + "
"; + +#end + +#formatting +print < + +END + +#subroutiens +sub keyfield_numerically { (split(/\t/,$a))[0] <=> (split(/\t/,$b))[0] ; } + -- cgit v1.2.1 -- cgit v1.2.1 From 13836fa8df9d3849f089d47342b25162327c28f7 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 3 Sep 1998 04:09:09 +0000 Subject: Initial revision --- htdocs/docs/billing.html | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 htdocs/docs/billing.html diff --git a/htdocs/docs/billing.html b/htdocs/docs/billing.html new file mode 100644 index 000000000..02bfbd783 --- /dev/null +++ b/htdocs/docs/billing.html @@ -0,0 +1,40 @@ + + Billing + + +

Billing

+ The bin/bill script can be run daily to bill all customers. Usage: bill [ -c [ i ] ] [ -d date ] [ -b ] +
    +
  • -c: Turn on collecting (you probably want this). +
  • -i: Real-time billing (as opposed to bacth billing). Only relevant for credit cards. Not available without modifying site_perl/Bill.pm +
  • -d: Pretend it is date (parsed by Date::Parse) +
  • -b: N/A +
+ Printing should be configured on your freeside machine to print invoices. +

Batch credit card processing +
    +
  • After this script is run, a credit card batch will be in the cust_pay_batch table. Export this table to your credit card batching. +
  • When your batch completes, erase the cust_pay_batch records in that batch and add any necessary paymants to the cust_pay table. Example code to add payments is: +
    use FS::cust_pay;
    +
    +# loop over all records in batch
    +
    +my $payment=create FS::cust_pay (
    +  'invnum' => $invnum,
    +  'paid' => $paid,
    +  '_date' => $_date,
    +  'payby' => $payby,
    +  'payinfo' => $payinfo,
    +  'paybatch' => $paybatch,
    +);
    +
    +my $error=$payment->insert;
    +if ( $error ) {
    +  #process error
    +}
    +
    +# end loop
    +
    +All fields except paybatch are contained in the cust_pay_batch table. You can use paybatch field to track particular batches and/or particular transactions within a batch. +
+ -- cgit v1.2.1 -- cgit v1.2.1 From 75764c551a19084d32cf424bdedb23c154973204 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 3 Sep 1998 04:50:13 +0000 Subject: Initial revision --- CREDITS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 CREDITS diff --git a/CREDITS b/CREDITS new file mode 100644 index 000000000..87c79a779 --- /dev/null +++ b/CREDITS @@ -0,0 +1,14 @@ +Thanks to Matt Simerson of MichWeb Inc. for documentation +and pre-release testing. Without his help the documentation in the first +release would have consisted of a single screenfull of text. + +# Steve Cleff did the default background image and is also +# the creator of Freeside's mascot, Snakeman. + +Jerry St. Pierre did the "SISD" graphic. + +Brian McCane? contributed PostgreSQL support, HTML +style enhancements and many, many bugfixes. + +Everything else is my (Ivan Kohler ) fault. + -- cgit v1.2.1 -- cgit v1.2.1 From ea7438fdd7db7793dbb2e4413daf525210749153 Mon Sep 17 00:00:00 2001 From: ivan Date: Sat, 12 Sep 1998 21:33:34 +0000 Subject: Initial revision --- htdocs/docs/trouble.html | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 htdocs/docs/trouble.html diff --git a/htdocs/docs/trouble.html b/htdocs/docs/trouble.html new file mode 100644 index 000000000..2cf6d4e71 --- /dev/null +++ b/htdocs/docs/trouble.html @@ -0,0 +1,41 @@ + + Troubleshooting + + +

Troubleshooting

+
    +
  • When troubleshooting the web interface, helpful information is often in your web server's error log. +
  • Internet Explorer will not work with Freeside's HTML interface. +Netscape, +Lynx, and +Emacs/W3, +among others, should work fine. +
  • If bin/svc_acct.import fails with an "Out of memory!" error using MySQL, upgrede MySQL and recompile the Perl DBD. There was a memory leak in some older versions of MySQL. +
  • If you get tons of errors in your web server's error log like this: +
    +Ambiguous use of value => resolved to "value" =>
    +at /usr/lib/perl5/site_perl/File/CounterFile.pm line 132.
    +
    + This clutters up your log files but is otherwise harmless. Upgrade to the latest File::CounterFile. +
  • If you get an Internal Server Error when adding or editing, but find that the update has occured, and you get something like the following in your web server's error log: +
    +access to /your/path/edit/process/some_table.cgi failed for
    +machine.domain.tld, reason: malformed header from script.
    +Bad header=HTTP/1.0 302 Moved Temporarily
    +
    + Then you forgot to apply this patch as mentioned in the New Installation section of the documentation. +
  • If you get errors like this: +
    +UID.pm: Can't open /var/spool/freeside/conf/secrets: Permission denied 
    +at /your/path/site_perl/FS/UID.pm line 26.
    +BEGIN failed--compilation aborted at
    +/your/path/edit/process/part_svc.cgi line 15.
    +
    + Then the scripts are not running setuid freeside. If you were editing +the files, it is possible you inadvertantly removed the setuid bit. +As mentioned in the New Installation section of the documentation, set ownership and permissions for the web interface. Your system should support secure setuid scripts or Perl's emulation, see perlsec: Security Bugs for information and workarounds. +
    cd /usr/local/apache/htdocs/freeside
    +chown -R freeside .
    +chmod 4755 browse/*.cgi edit/*.cgi edit/process/*.cgi misc/*.cgi misc/process/*.cgi search/*.cgi view/*.cgi
    +
+ -- cgit v1.2.1 -- cgit v1.2.1 From 361f95491952b2d968152ca50a94c7eec56610b1 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 18 Sep 1998 02:07:23 +0000 Subject: Initial revision --- htdocs/docs/install.html | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 htdocs/docs/install.html diff --git a/htdocs/docs/install.html b/htdocs/docs/install.html new file mode 100644 index 000000000..c4784ebf6 --- /dev/null +++ b/htdocs/docs/install.html @@ -0,0 +1,56 @@ + + Installation + + +

Installation

+Before installing, you need: + +Install the Freeside distribution: +
    +
  • Add the user `freeside' to your system. +
  • Add the freeside database to your database engine. (with MySQL) (with PostgreSQL) +
  • Allow the freeside user full access to the freeside database. (with MySQL) (with PostgreSQL) +
  • Unpack the tarball:
    gunzip -c fs-x.y.z.tar.gz | tar xvf -
    +
  • Copy or link fs-x.y.z/site_perl to FS in your site_perl directory. (try `perl -V' if unsure)
    mkdir /usr/local/lib/site_perl/FS
    +cp fs-x.y.z/site_perl/* /usr/local/lib/site_perl/FS
    or
    ln -s /full/path/to/fs-x.y.z/site_perl /usr/local/lib/site_perl/FS
    +
  • Copy or link fs-x.y.z/htdocs to your web server's document space.
    mkdir /usr/local/apache/htdocs/freeside
    +cp -r fs-x.y.z/htdocs/* /usr/local/apache/htdocs/freeside
    or
    ln -s /full/path/to/fs-x.y.z/htdocs /usr/local/apache/htdocs/freeside
    +
  • Restrict access to this web interface. (with Apache) +
  • Enable CGI execution for files with the `.cgi' extension. (with Apache) +
  • Set ownership and permissions for the web interface. Your system should support secure setuid scripts or Perl's emulation, see perlsec: Security Bugs for information and workarounds. +
    cd /usr/local/apache/htdocs/freeside
    +chown -R freeside .
    +chmod 4755 browse/*.cgi edit/*.cgi edit/process/*.cgi misc/*.cgi misc/process/*.cgi search/*.cgi view/*.cgi
    +
  • Create the base Freeside directory `/var/spool/freeside', and the subdirectories `conf', `counters', and `export'.
    mkdir /var/spool/freeside
    +mkdir /var/spool/freeside/conf
    +mkdir /var/spool/freeside/counters
    +mkdir /var/spool/freeside/export
    +chown -R freeside /var/spool/freeside
    +
  • Create the necessary configuration files. +
  • Run bin/fs-setup to create the database tables. +
+ -- cgit v1.2.1 -- cgit v1.2.1 From ab21af64cd80035d8f713e4704919f3b9733a936 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 18 Sep 1998 05:43:42 +0000 Subject: Initial revision --- bin/svc_acct.export | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100755 bin/svc_acct.export diff --git a/bin/svc_acct.export b/bin/svc_acct.export new file mode 100755 index 000000000..3f65a08ba --- /dev/null +++ b/bin/svc_acct.export @@ -0,0 +1,351 @@ +#!/usr/bin/perl -Tw +# +# Create and export password files: passwd, passwd.adjunct, shadow, +# acp_passwd, acp_userinfo, acp_dialup, users +# +# ivan@voicenet.com late august/september 96 +# (the password encryption bits were from melody) +# +# use a temporary copy of svc_acct to minimize lock time on the real file, +# and skip blank entries. +# +# ivan@voicenet.com 96-Oct-6 +# +# change users / acp_dialup file formats +# ivan@voicenet.com 97-jan-28-31 +# +# change priority (after copies) to 19, not 10 +# ivan@voicenet.com 97-feb-5 +# +# added exit if stuff is already locked 97-apr-15 +# +# rewrite ivan@sisd.com 98-mar-9 +# +# Changed 'password' to '_password' because Pg6.3 reserves this word +# Added code to create a FreeBSD style master.passwd file +# bmccane@maxbaud.net 98-Apr-3 +# +# don't export non-root 0 UID's, even if they get put in the database +# ivan@sisd.com 98-jul-14 +# +# Uses Idle_Timeout, Port_Limit, Framed_Netmask and Framed_Route if they +# exist; need some way to support arbitrary radius fields. also +# /var/spool/freeside/conf/ ivan@sisd.com 98-jul-26, aug-9 +# +# OOPS! added arbitrary radius fields (pry 98-aug-16) but forgot to say so. +# ivan@sisd.com 98-sep-18 + +use strict; +use Fcntl qw(:flock); +use FS::SSH qw(scp ssh); +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearch fields); + +my($fshellmachines)="/var/spool/freeside/conf/shellmachines"; +my(@shellmachines); +if ( -e $fshellmachines ) { + open(SHELLMACHINES,$fshellmachines); + @shellmachines=map { + /^(.*)$/ or die "Illegal line in conf/shellmachines"; #we trust the file + $1; + } grep $_ !~ /^(#|$)/, ; + close SHELLMACHINES; +} + +my($fbsdshellmachines)="/var/spool/freeside/conf/bsdshellmachines"; +my(@bsdshellmachines); +if ( -e $fbsdshellmachines ) { + open(BSDSHELLMACHINES,$fbsdshellmachines); + @bsdshellmachines=map { + /^(.*)$/ or die "Illegal line in conf/bsdshellmachines"; #we trust the file + $1; + } grep $_ !~ /^(#|$)/, ; + close BSDSHELLMACHINES; +} + +my($fnismachines)="/var/spool/freeside/conf/nismachines"; +my(@nismachines); +if ( -e $fnismachines ) { + open(NISMACHINES,$fnismachines); + @nismachines=map { + /^(.*)$/ or die "Illegal line in conf/nismachines"; #we trust the file + $1; + } grep $_ !~ /^(#|$)/, ; + close NISMACHINES; +} + +my($ferpcdmachines)="/var/spool/freeside/conf/erpcdmachines"; +my(@erpcdmachines); +if ( -e $ferpcdmachines ) { + open(ERPCDMACHINES,$ferpcdmachines); + @erpcdmachines=map { + /^(.*)$/ or die "Illegal line in conf/erpcdmachines"; #we trust the file + $1; + } grep $_ !~ /^(#|$)/, ; + close ERPCDMACHINES; +} + +my($fradiusmachines)="/var/spool/freeside/conf/radiusmachines"; +my(@radiusmachines); +if ( -e $fradiusmachines ) { + open(RADIUSMACHINES,$fradiusmachines); + @radiusmachines=map { + /^(.*)$/ or die "Illegal line in conf/radiusmachines"; #we trust the file + $1; + } grep $_ !~ /^(#|$)/, ; + close RADIUSMACHINES; +} + +my($spooldir)="/var/spool/freeside/export"; +my($spoollock)="/var/spool/freeside/svc_acct.export.lock"; + +adminsuidsetup; + +my(@saltset)= ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' ); +srand(time|$$); + +open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!"; +select(EXPORT); $|=1; select(STDOUT); +unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) { + seek(EXPORT,0,0); + my($pid)=; + chop($pid); + #no reason to start loct of blocking processes + die "Is another export process running under pid $pid?\n"; +} +seek(EXPORT,0,0); +print EXPORT $$,"\n"; + +my(@svc_acct)=qsearch('svc_acct',{}); + +( open(MASTER,">$spooldir/master.passwd") + and flock(MASTER,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/master.passwd: $!"; +( open(PASSWD,">$spooldir/passwd") + and flock(PASSWD,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/passwd: $!"; +( open(SHADOW,">$spooldir/shadow") + and flock(SHADOW,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/shadow: $!"; +( open(ACP_PASSWD,">$spooldir/acp_passwd") + and flock (ACP_PASSWD,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/acp_passwd: $!"; +( open (ACP_DIALUP,">$spooldir/acp_dialup") + and flock(ACP_DIALUP,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/acp_dialup: $!"; +( open (USERS,">$spooldir/users") + and flock(USERS,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/users: $!"; + +chmod 0644, "$spooldir/passwd", + "$spooldir/acp_dialup", +; +chmod 0600, "$spooldir/master.passwd", + "$spooldir/acp_passwd", + "$spooldir/shadow", + "$spooldir/users", +; + +setpriority(0,0,10); + +my($svc_acct); +foreach $svc_acct (@svc_acct) { + + my($password)=$svc_acct->getfield('_password'); + my($cpassword,$rpassword); + if ( ( length($password) <= 8 ) + && ( $password ne '*' ) + && ( $password ne '' ) + ) { + $cpassword=crypt($password, + $saltset[int(rand(64))].$saltset[int(rand(64))] + ); + $rpassword=$password; + } else { + $cpassword=$password; + $rpassword='UNIX'; + } + + if ( $svc_acct->uid =~ /^(\d+)$/ ) { + + die "Non-root user ". $svc_acct->username. " has 0 UID!" + if $svc_acct->uid == 0 && $svc_acct->username ne 'root'; + + ### + # FORMAT OF FreeBSD MASTER PASSWD FILE HERE + print MASTER join(":", + $svc_acct->username, # User name + $cpassword, # Encrypted password + $svc_acct->uid, # User ID + $svc_acct->gid, # Group ID + "", # Login Class + "0", # Password Change Time + "0", # Password Expiration Time + $svc_acct->finger, # Users name + $svc_acct->dir, # Users home directory + $svc_acct->shell, # shell + ), "\n" ; + + ### + # FORMAT OF THE PASSWD FILE HERE + print PASSWD join(":", + $svc_acct->username, + 'x', # "##". $svc_acct->$username, + $svc_acct->uid, + $svc_acct->gid, + $svc_acct->finger, + $svc_acct->dir, + $svc_acct->shell, + ), "\n"; + + ### + # FORMAT OF THE SHADOW FILE HERE + print SHADOW join(":", + $svc_acct->username, + $cpassword, + '', + '', + '', + '', + '', + '', + '', + ), "\n"; + + } + + if ( $svc_acct->slipip ne '' ) { + + ### + # FORMAT OF THE ACP_* FILES HERE + print ACP_PASSWD join(":", + $svc_acct->username, + $cpassword, + "0", + "0", + "", + "", + "", + ), "\n"; + + my($ip)=$svc_acct->slipip; + + unless ( $ip eq '0.0.0.0' || $svc_acct->slipip eq '0e0' ) { + print ACP_DIALUP $svc_acct->username, "\t*\t", $svc_acct->slipip, "\n"; + } + + ### + # FORMAT OF THE USERS FILE HERE + print USERS + $svc_acct->username, qq(\tPassword = "$rpassword"\n\t), + + join ",\n\t", + map { + /^(radius_(.*))$/; + my($field,$attrib)=($1,$2); + $attrib =~ s/_/\-/g; + "$attrib = \"". $svc_acct->getfield($field). "\""; + } grep /^radius_/ && $svc_acct->getfield($_), fields('svc_acct') + ; + if ( $ip && $ip ne '0e0' ) { + print USERS qq(,\n\tFramed-Address = "$ip"\n\n); + } else { + print USERS qq(\n\n); + } + + } + +} + +flock(MASTER,LOCK_UN); +flock(PASSWD,LOCK_UN); +flock(SHADOW,LOCK_UN); +flock(ACP_DIALUP,LOCK_UN); +flock(ACP_PASSWD,LOCK_UN); +flock(USERS,LOCK_UN); + +close MASTER; +close PASSWD; +close SHADOW; +close ACP_DIALUP; +close ACP_PASSWD; +close USERS; + +### +# export stuff +# + +my($shellmachine); +foreach $shellmachine (@shellmachines) { + scp("$spooldir/passwd","root\@$shellmachine:/etc/passwd.new") + == 0 or die "scp error: $!"; + scp("$spooldir/shadow","root\@$shellmachine:/etc/shadow.new") + == 0 or die "scp error: $!"; + ssh("root\@$shellmachine", + "( ". + "mv /etc/passwd.new /etc/passwd; ". + "mv /etc/shadow.new /etc/shadow; ". + " )" + ) + == 0 or die "ssh error: $!"; +} + +my($bsdshellmachine); +foreach $bsdshellmachine (@bsdshellmachines) { + scp("$spooldir/passwd","root\@$bsdshellmachine:/etc/passwd.new") + == 0 or die "scp error: $!"; + scp("$spooldir/master.passwd","root\@$bsdshellmachine:/etc/master.passwd.new") + == 0 or die "scp error: $!"; + ssh("root\@$bsdshellmachine", + "( ". + "mv /etc/passwd.new /etc/passwd; ". + "mv /etc/master.passwd.new /etc/master.passwd; ". + " )" + ) + == 0 or die "ssh error: $!"; +} + +my($nismachine); +foreach $nismachine (@nismachines) { + scp("$spooldir/passwd","root\@$nismachine:/etc/global/passwd") + == 0 or die "scp error: $!"; + scp("$spooldir/shadow","root\@$nismachine:/etc/global/shadow") + == 0 or die "scp error: $!"; + ssh("root\@$nismachine", + "( ". + "cd /var/yp; make; ". + " )" + ) + == 0 or die "ssh error: $!"; +} + +my($erpcdmachine); +foreach $erpcdmachine (@erpcdmachines) { + scp("$spooldir/acp_passwd","root\@$erpcdmachine:/usr/annex/acp_passwd") + == 0 or die "scp error: $!"; + scp("$spooldir/acp_dialup","root\@$erpcdmachine:/usr/annex/acp_dialup") + == 0 or die "scp error: $!"; + ssh("root\@$erpcdmachine", + "( ". + "kill -USR1 \`cat /usr/annex/erpcd.pid\'". + " )" + ) + == 0 or die "ssh error: $!"; +} + +my($radiusmachine); +foreach $radiusmachine (@radiusmachines) { + scp("$spooldir/users","root\@$radiusmachine:/etc/raddb/users") + == 0 or die "scp error: $!"; + ssh("root\@$erpcdmachine", + "( ". + "builddbm". + " )" + ) + == 0 or die "ssh error: $!"; +} + +unlink $spoollock; +flock(EXPORT,LOCK_UN); +close EXPORT; + -- cgit v1.2.1 -- cgit v1.2.1 From c0af1d666cd655dcece6f263890123851631c0fd Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 18 Sep 1998 22:14:43 +0000 Subject: Initial revision --- htdocs/docs/schema.html | 205 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 htdocs/docs/schema.html diff --git a/htdocs/docs/schema.html b/htdocs/docs/schema.html new file mode 100644 index 000000000..5a296ec83 --- /dev/null +++ b/htdocs/docs/schema.html @@ -0,0 +1,205 @@ + + Schema reference + + +

Schema reference

+
    +
  • agent - Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their agent type). +
      +
    • agentnum - primary key +
    • agent - name of this agent +
    • typenum - agent type +
    • prog - (unimplemented) +
    • freq - (unimplemented) +
    +
  • agent_type - Agent types define groups of packages that you can then assign to particular agents. +
      +
    • typenum - primary key +
    • atype - name of this agent type +
    +
  • cust_bill - Invoices +
      +
    • invnum - primary key +
    • custnum - customer +
    • _date +
    • charged - amount of this invoice +
    • owed - amount still outstanding on this invoice +
    • printed - how many times this invoice has been printed automatically +
    +
  • cust_bill_pkg - Invoice line items +
      +
    • invnum - (multiple) key +
    • pkgnum - package +
    • setup - setup fee +
    • recur - recurring fee +
    • sdate - starting date +
    • edate - ending date +
    +
  • cust_credit - Credits +
      +
    • crednum - primary key +
    • custnum - customer +
    • amount - amount credited +
    • credited - amount still outstanding (not yet refunded) on this credit +
    • _date +
    • otaker - order taker +
    • reason +
    +
  • cust_main - Customers +
      +
    • custnum - primary key +
    • agentnum - agent +
    • refnum - referral +
    • first - name +
    • last - name +
    • ss - social security number +
    • company +
    • address1 +
    • address2 +
    • city +
    • county +
    • state +
    • zip +
    • country +
    • daytime - phone +
    • night - phone +
    • payby - CARD, BILL, or COMP +
    • payinfo - card number, P.O.#, or comp issuer +
    • paydate - expiration date +
    • payname - billing name (name on card) +
    • tax - tax exempt, Y or null +
    • otaker - order taker +
    +
  • cust_main_county - Tax rates +
      +
    • taxnum - primary key +
    • state +
    • county +
    • tax - % rate +
    +
  • cust_pay - Payments +
      +
    • paynum - primary key +
    • invnum - invoice +
    • paid - amount +
    • _date +
    • payby - CARD, BILL, or COMP +
    • payinfo - card number, P.O.#, or comp issuer +
    • paybatch - text field for tracking card processor batches +
    +
  • cust_pay_batch - Pending batch +
      +
    • trancode - 77 for charges +
    • cardnum +
    • exp - card expiration +
    • amount +
    • invnum - invoice +
    • custnum - customer +
    • payname - name on card +
    • first - name +
    • last - name +
    • address1 +
    • address2 +
    • city +
    • state +
    • zip +
    • country +
    +
  • cust_pkg - Customer billing items +
      +
    • pkgnum - primary key +
    • custnum - customer +
    • pkgpart - Package definition +
    • setup - date +
    • bill - next bill date +
    • susp - (past) suspension date +
    • expire - (future) cancellation date +
    • cancel - (past) cancellation date +
    • otaker - order taker +
    +
  • cust_refund - Refunds +
      +
    • refundnum - primary key +
    • crednum - credit +
    • refund - amount +
    • _date +
    • payby - CARD, BILL or COMP +
    • payinfo - card number, P.O.#, or comp issuer +
    • otaker - order taker +
    +
  • cust_svc - Customer services + +
  • part_pkg - Package definitions +
      +
    • pkgpart - primary key +
    • pkg - package name +
    • comment - non-customer visable package comment +
    • setup - setup fee +
    • freq - recurring frequency (months) +
    • recur - recurring fee +
    +
  • part_referral - Referral listing +
      +
    • refnum
    • - primary key +
    • referral
    • - referral +
    +
  • part_svc - Service definitions +
      +
    • svcpart - primary key +
    • svc - name of this service +
    • svcdb - table used for this service: svc_acct, svc_acct_sm, svc_domain, svc_charge or svc_wo +
    • table__field - Default or fixed value for field in table +
    • table__field_flag - null, D or F +
    +
  • pkg_svc + +
  • svc_acct - Accounts +
      +
    • svcnum - primary key +
    • username +
    • _password +
    • popnum - Point of Presence +
    • uid +
    • gid +
    • finger - GECOS +
    • dir +
    • shell +
    • quota - (unimplementd) +
    • slipip - IP address +
    • radius_Radius_Attribute - Radius-Attribute +
    +
  • svc_acct_pop - Points of Presence +
      +
    • popnum - primary key +
    • city +
    • state +
    • ac - area code +
    • exch - exchange +
    +
  • svc_acct_sm - Domain mail aliases + +
  • svc_domain - Domains + +
  • type_pkgs + +
+ -- cgit v1.2.1 -- cgit v1.2.1 From 193a5aaa811933f68cfcb0f32a26b4769c640a4b Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 20 Sep 1998 19:53:32 +0000 Subject: Initial revision --- site_perl/Bill.pm | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 site_perl/Bill.pm diff --git a/site_perl/Bill.pm b/site_perl/Bill.pm new file mode 100644 index 000000000..4d7e059ed --- /dev/null +++ b/site_perl/Bill.pm @@ -0,0 +1,44 @@ +package FS::Bill; + +use strict; +use vars qw(@ISA); +use FS::cust_main; + +@ISA = qw(FS::cust_main); + +warn "FS::Bill depriciated\n"; + +=head1 NAME + +FS::Bill - Legacy stub + +=head1 SYNOPSIS + +The functionality of FS::Bill has been integrated into FS::cust_main. + +=head1 HISTORY + +ivan@voicenet.com 97-jul-24 - 25 - 28 + +use Safe; evaluate all fees with perl (still on TODO list until I write +some examples & test opmask to see if we can read db) +%hash=$obj->hash later ivan@sisd.com 98-mar-13 + +packages with no next bill date start at $time not time, this should +eliminate the last of the problems with billing at a past date +also rewrite the invoice priting logic not to print invoices for things +that haven't happended yet and update $cust_bill->printed when we print +so PAST DUE notices work, and s/date/_date/ +ivan@sisd.com 98-jun-4 + +more logic for past due stuff - packages with no next bill date start +at $cust_pkg->setup || $time ivan@sisd.com 98-jul-13 + +moved a few things in collection logic; negative charges should work +ivan@sisd.com 98-aug-6 + +pod, moved everything to FS::cust_main ivan@sisd.com 98-sep-19 + +=cut + +1; -- cgit v1.2.1 -- cgit v1.2.1 From 45373022fa23a4e9cea0e15346c9e834c209be03 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 21 Sep 1998 23:01:49 +0000 Subject: Initial revision --- htdocs/docs/config.html | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 htdocs/docs/config.html diff --git a/htdocs/docs/config.html b/htdocs/docs/config.html new file mode 100644 index 000000000..9b8002601 --- /dev/null +++ b/htdocs/docs/config.html @@ -0,0 +1,38 @@ + + Configuration files + + +

Configuration files

+Configuration files and directories are located in `/var/spool/freeside/conf'. +
    +
  • address - Your company name and address, four lines. +
  • bsdshellmachines - Your BSD flavored shell (and mail) machines, one per line. This enables export of `/etc/passwd' and `/etc/master.passwd'. +
  • cybercash2 - CyberCash v2 support, four lines: paymentserverhost, paymentserverport, paymentserversecret, and transaction type (`mauthonly' or `mauthcapture'). CCLib.pm is required. +
  • cybercash3.2 - CyberCash v3.2 support. Two lines: the full path and name of your merchant_conf file, and the transaction type (`mauthonly' or `mauthcapture'). CCMckLib3_2.pm, CCMckDirectLib3_2.pm and CCMckErrno3_2 are required. +
  • domain - Your domain name. +
  • erpcdmachines - Your ERPCD authenticaion machines, one per line. This enables export of `/usr/annex/acp_passwd' and `/usr/annex/acp_dialup'. +
  • home - For new users, prefixed to usrename to create a directory name. Should have a leading but not a trailing slash. +
  • lpr - Print command for paper invoices, for example `lpr -h'. +
  • nismachines - Your NIS master (not slave master) machines, one per line. This enables export of `/etc/global/passwd' and `/etc/global/shadow'. +
  • qmailmachines - Your qmail machines, one per line. This enables export of `/var/qmail/control/virtualdomains', `/var/qmail/control/recipientmap', and `/var/qmail/control/rcpthosts'. The existance of this file (even if empty) also turns on user `.qmail-extension' file maintenance in conjunction with `shellmachine'. +
  • radiusmachines - Your RADIUS authentication machines, one per line. This enables export of `/etc/raddb/users'. +
  • registries - Directory which contains domain registry information. Each registry is a directory. +
      +
    • registries/internic - Currently the only supported registry +
        +
      • registries/internic/from - Email address from which InterNIC domain registrations are sent. +
      • regestries/internic/nameservers - The nameservers for InterNIC domain registrations, one per line. Each line contains an IP address and hostname, separated by whitespace. +
      • registries/internic/tech_contact - Technical contact NIC handle for domain registrations. +
      • registries/internic/template - Template for InterNIC domain registrations with special markup. A suitable copy of the InterNIC domain template v4.0 is in `fs-x.y.z/etc/domain-template.txt'. +
      • registries/internic/to - Email address to which InterNIC domain registrations are sent. +
      +
    +
  • secrets - Three lines: Database engine datasource (for example, `DBI:mysql:freeside' or `DBI:Pg:dbname=freeside'), username, and password. This file should not be world readable. +
  • sendmailmachines - Your sendmail machines, one per line. This enables export of `/etc/virtusertable' and `/etc/sendmail.cw'. +
  • shellmachine - A single machine with user home directories mounted. This enables home directory creation, renaming and archiving/deletion. In conjunction with `qmailmachines', it also enables `.qmail-extension' file maintenance. +
  • shellmachines - Your Linux and System V flavored shell (and mail) machines, one per line. This enables export of `/etc/passwd' and `/etc/shadow' files. +
  • shells - Legal shells (think /etc/shells). You probably want to `cut -d: -f7 /etc/passwd | sort | uniq' initially so that importing doesn't fail with `Illegal shell' errors, then remove any special entries afterwords. A blank line specifies that an empty shell is permitted. +
  • smtpmachine - SMTP relay for Freeside's outgoing mail. +
+ + -- cgit v1.2.1 -- cgit v1.2.1 From 29c26d9aa05185d7cd887d1d7c48e70f82fa27d2 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 21 Sep 1998 23:49:23 +0000 Subject: Initial revision --- site_perl/CGI.pm | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 site_perl/CGI.pm diff --git a/site_perl/CGI.pm b/site_perl/CGI.pm new file mode 100644 index 000000000..d2ed52122 --- /dev/null +++ b/site_perl/CGI.pm @@ -0,0 +1,143 @@ +package FS::CGI; + +use strict; +use vars qw(@EXPORT_OK @ISA); +use Exporter; +use CGI::Base; +use CGI::Carp qw(fatalsToBrowser); + +@ISA = qw(Exporter); +@EXPORT_OK = qw(header menubar idiot eidiot); + +=head1 NAME + +FS::CGI - Subroutines for the web interface + +=head1 SYNOPSIS + + use FS::CGI qw(header menubar idiot eidiot); + + print header( 'Title', '' ); + print header( 'Title', menubar('item', 'URL', ... ) ); + + idiot "error message"; + eidiot "error message"; + +=head1 DESCRIPTION + +Provides a few common subroutines for the web interface. + +=head1 SUBROUTINES + +=over 4 + +=item header TITLE, MENUBAR + +Returns an HTML header. + +=cut + +sub header { + my($title,$menubar)=@_; + + < + + + $title + + + +
+

+ $title +

+ $menubar +
+
+END +} + +=item menubar ITEM, URL, ... + +Returns an HTML menubar. + +=cut + +sub menubar { #$menubar=menubar('Main Menu', '../', 'Item', 'url', ... ); + my($item,$url,@html); + while (@_) { + ($item,$url)=splice(@_,0,2); + push @html, qq!$item!; + } + join(' | ',@html); +} + +=item idiot ERROR + +Sends headers and an HTML error message. + +=cut + +sub idiot { + my($error)=@_; + CGI::Base::SendHeaders(); + print < + + Error processing your request + + +
+

Error processing your request

+
+ Your request could not be processed because of the following error: +

$error +

Hit the Back button in your web browser, correct this mistake, and try again. + + +END + +} + +=item eidiot ERROR + +Sends headers and an HTML error message, then exits. + +=cut + +sub eidiot { + idiot(@_); + exit; +} + +=back + +=head1 BUGS + +Not OO. + +Not complete. + +Uses CGI-modules instead of CGI.pm + +=head1 SEE ALSO + +L + +=head1 HISTORY + +subroutines for the HTML/CGI GUI, not properly OO. :( + +ivan@sisd.com 98-apr-16 +ivan@sisd.com 98-jun-22 + +lose the background, eidiot ivan@sisd.com 98-sep-2 + +pod ivan@sisd.com 98-sep-12 + +=cut + +1; + + -- cgit v1.2.1 -- cgit v1.2.1 From 2d294f8309b99f02d895b2110c73f1295dab9138 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 21 Sep 1998 23:58:53 +0000 Subject: Initial revision --- site_perl/SSH.pm | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 site_perl/SSH.pm diff --git a/site_perl/SSH.pm b/site_perl/SSH.pm new file mode 100644 index 000000000..d5a0df654 --- /dev/null +++ b/site_perl/SSH.pm @@ -0,0 +1,157 @@ +package FS::SSH; + +use strict; +use vars qw(@ISA @EXPORT_OK $ssh $scp); +use Exporter; +use IPC::Open2; +use IPC::Open3; + +@ISA = qw(Exporter); +@EXPORT_OK = qw(ssh scp issh iscp sshopen2 sshopen3); + +$ssh="ssh"; +$scp="scp"; + +=head1 NAME + +FS::SSH - Subroutines to call ssh and scp + +=head1 SYNOPSIS + + use FS::SSH qw(ssh scp issh iscp sshopen2 sshopen3); + + ssh($host, $command); + + issh($host, $command); + + scp($source, $destination); + + iscp($source, $destination); + + sshopen2($host, $reader, $writer, $command); + + sshopen3($host, $reader, $writer, $error, $command); + +=head1 DESCRIPTION + + Simple wrappers around ssh and scp commands. + +=head1 SUBROUTINES + +=over 4 + +=item ssh HOST, COMMAND + +Calls ssh in batch mode. + +=cut + +sub ssh { + my($host,$command)=@_; + my(@cmd)=($ssh, "-o", "BatchMode yes", $host, $command); +# print join(' ',@cmd),"\n"; +#0; + system(@cmd); +} + +=item issh HOST, COMMAND + +Prints the ssh command to be executed, waits for the user to confirm, and +(optionally) executes the command. + +=cut + +sub issh { + my($host,$command)=@_; + my(@cmd)=($ssh, $host, $command); + print join(' ',@cmd),"\n"; + if ( &_yesno ) { + ###print join(' ',@cmd),"\n"; + system(@cmd); + } +} + +=item scp SOURCE, DESTINATION + +Calls scp in batch mode. + +=cut + +sub scp { + my($src,$dest)=@_; + my(@cmd)=($scp,"-Bprq",$src,$dest); +# print join(' ',@cmd),"\n"; +#0; + system(@cmd); +} + +=item iscp SOURCE, DESTINATION + +Prints the scp command to be executed, waits for the user to confirm, and +(optionally) executes the command. + +=cut + +sub iscp { + my($src,$dest)=@_; + my(@cmd)=($scp,"-pr",$src,$dest); + print join(' ',@cmd),"\n"; + if ( &_yesno ) { + ###print join(' ',@cmd),"\n"; + system(@cmd); + } +} + +=item sshopen2 HOST, READER, WRITER, COMMAND + +Connects the supplied filehandles to the ssh process (in batch mode). + +=cut + +sub sshopen2 { + my($host,$reader,$writer,$command)=@_; + open2($reader,$writer,$ssh,'-o','Batchmode yes',$host,$command); +} + +=item sshopen3 HOST, WRITER, READER, ERROR, COMMAND + +Connects the supplied filehandles to the ssh process (in batch mode). + +=cut + +sub sshopen3 { + my($host,$writer,$reader,$error,$command)=@_; + open3($writer,$reader,$error,$ssh,'-o','Batchmode yes',$host,$command); +} + +sub _yesno { + print "Proceed [y/N]:"; + my($x)=scalar(); + $x =~ /^y/i; +} + +=head1 BUGS + +Not OO. + +scp stuff should transparantly use rsync-over-ssh instead. + +=head1 SEE ALSO + +L, L, L, L + +=head1 HISTORY + +ivan@voicenet.com 97-jul-17 + +added sshopen2 and sshopen3 ivan@sisd.com 98-mar-9 + +added iscp ivan@sisd.com 98-jul-25 +now iscp asks y/n, issh and took out path ivan@sisd.com 98-jul-30 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 4478d24bd8f66aaff7e2f304d3c2fe2cc1d3bf04 Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 22 Sep 1998 02:57:14 +0000 Subject: Initial revision --- site_perl/agent.pm | 166 +++++++++++++++++ site_perl/cust_bill.pm | 495 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 661 insertions(+) create mode 100644 site_perl/agent.pm create mode 100644 site_perl/cust_bill.pm diff --git a/site_perl/agent.pm b/site_perl/agent.pm new file mode 100644 index 000000000..7fc370ed0 --- /dev/null +++ b/site_perl/agent.pm @@ -0,0 +1,166 @@ +package FS::agent; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields qsearch qsearchs); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +=head1 NAME + +FS::agent - Object methods for agent records + +=head1 SYNOPSIS + + use FS::agent; + + $record = create FS::agent \%hash; + $record = create FS::agent { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::agent object represents an agent. Every customer has an agent. Agents +can be used to track things like resellers or salespeople. FS::agent inherits +from FS::Record. The following fields are currently supported: + +=over 4 + +=item agemtnum - primary key (assigned automatically for new agents) + +=item agent - Text name of this agent + +=item typenum - Agent type. See L + +=item prog - For future use. + +=item freq - For future use. + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new agent. To add the agent to the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('agent')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('agent',$hashref); +} + +=item insert + +Adds this agent to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Deletes this agent from the database. Only agents with no customers can be +deleted. If there is an error, returns the error, otherwise returns false. + +=cut + +sub delete { + my($self)=@_; + return "Can't delete an agent with customers!" + if qsearch('cust_main',{'agentnum' => $self->agentnum}); + $self->del; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not an agent record!" unless $old->table eq "agent"; + return "Can't change agentnum!" + unless $old->getfield('agentnum') eq $new->getfield('agentnum'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid agent. If there is an error, +returns the error, otherwise returns false. Called by the insert and replace +methods. + +=cut + +sub check { + my($self)=@_; + return "Not a agent record!" unless $self->table eq "agent"; + + my($error)= + $self->ut_numbern('agentnum') + or $self->ut_text('agent') + or $self->ut_number('typenum') + or $self->ut_numbern('freq') + or $self->ut_textn('prog') + ; + return $error if $error; + + return "Unknown typenum!" + unless qsearchs('agent_type',{'typenum'=> $self->getfield('typenum') }); + + ''; + +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=head1 HISTORY + +Class dealing with agent (resellers) + +ivan@sisd.com 97-nov-13, 97-dec-10 + +pod, added check in ->delete ivan@sisd.com 98-sep-22 + +=cut + +1; + diff --git a/site_perl/cust_bill.pm b/site_perl/cust_bill.pm new file mode 100644 index 000000000..00234519a --- /dev/null +++ b/site_perl/cust_bill.pm @@ -0,0 +1,495 @@ +package FS::cust_bill; + +use strict; +use vars qw(@ISA $conf $add1 $add2 $add3 $add4); +use Exporter; +use Date::Format; +use FS::Record qw(fields qsearch qsearchs); + +@ISA = qw(FS::Record Exporter); + +$conf = new FS::Conf; + +($add1,$add2,$add3,$add4) = $conf->config('address'); + +=head1 NAME + +FS::cust_bill - Object methods for cust_bill records + +=head1 SYNOPSIS + + use FS::cust_bill; + + $record = create FS::cust_bill \%hash; + $record = create FS::cust_bill { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + + ( $total_previous_balance, @previous_cust_bill ) = $record->previous; + + @cust_bill_pkg_objects = $cust_bill->cust_bill_pkg; + + ( $total_previous_credits, @previous_cust_credit ) = $record->cust_credit; + + @cust_pay_objects = $cust_bill->cust_pay; + + @lines = $cust_bill->print_text; + @lines = $cust_bill->print_text $time; + +=head1 DESCRIPTION + +An FS::cust_bill object represents an invoice. FS::cust_bill inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item invnum - primary key (assigned automatically for new invoices) + +=item custnum - customer (see L) + +=item _date - specified as a UNIX timestamp; see L. Also see +L and L for conversion functions. + +=item charged - amount of this invoice + +=item owed - amount still outstanding on this invoice, which is charged minus +all payments (see L). + +=item printed - how many times this invoice has been printed automatically +(see L). + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new invoice. To add the invoice to the database, see L<"insert">. +Invoices are normally created by calling the bill method of a customer object +(see L). + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('cust_bill')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('cust_bill',$hashref); +} + +=item insert + +Adds this invoice to the database ("Posts" the invoice). If there is an error, +returns the error, otherwise returns false. + +When adding new invoices, owed must be charged (or null, in which case it is +automatically set to charged). + +=cut + +sub insert { + my($self)=@_; + + $self->setfield('owed',$self->charged) if $self->owed eq ''; + return "owed != charged!" + unless $self->owed == $self->charged; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. I don't remove invoices because there would then be +no record you ever posted this invoice (which is bad, no?) + +=cut + +sub delete { + return "Can't remove invoice!" + #my($self)=@_; + #$self->del; +} + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +Only owed and printed may be changed. Owed is normally updated by creating and +inserting a payment (see L). Printed is normally updated by +calling the collect method of a customer object (see L). + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a cust_bill record!" unless $old->table eq "cust_bill"; + return "Can't change invnum!" + unless $old->getfield('invnum') eq $new->getfield('invnum'); + return "Can't change custnum!" + unless $old->getfield('custnum') eq $new->getfield('custnum'); + return "Can't change _date!" + unless $old->getfield('_date') eq $new->getfield('_date'); + return "Can't change charged!" + unless $old->getfield('charged') eq $new->getfield('charged'); + return "(New) owed can't be > (new) charged!" + if $new->getfield('owed') > $new->getfield('charged'); + + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid invoice. If there is an error, +returns the error, otherwise returns false. Called by the insert and replace +methods. + +=cut + +sub check { + my($self)=@_; + return "Not a cust_bill record!" unless $self->table eq "cust_bill"; + my($recref) = $self->hashref; + + $recref->{invnum} =~ /^(\d*)$/ or return "Illegal invnum"; + $recref->{invnum} = $1; + + $recref->{custnum} =~ /^(\d+)$/ or return "Illegal custnum"; + $recref->{custnum} = $1; + return "Unknown customer" + unless qsearchs('cust_main',{'custnum'=>$recref->{custnum}}); + + $recref->{_date} =~ /^(\d*)$/ or return "Illegal date"; + $recref->{_date} = $recref->{_date} ? $1 : time; + + #$recref->{charged} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal charged"; + $recref->{charged} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal charged"; + $recref->{charged} = $1; + + $recref->{owed} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal owed"; + $recref->{owed} = $1; + + $recref->{printed} =~ /^(\d*)$/ or return "Illegal printed"; + $recref->{printed} = $1 || '0'; + + ''; #no error +} + +=item previous + +Returns a list consisting of the total previous balance for this customer, +followed by the previous outstanding invoices (as FS::cust_bill objects also). + +=cut + +sub previous { + my($self)=@_; + my($total)=0; + my(@cust_bill) = sort { $a->_date <=> $b->_date } + grep { $_->owed != 0 && $_->_date < $self->_date } + qsearch('cust_bill',{ 'custnum' => $self->custnum } ) + ; + foreach (@cust_bill) { $total += $_->owed; } + $total, @cust_bill; +} + +=item cust_bill_pkg + +Returns the line items (see L) for this invoice. + +=cut + +sub cust_bill_pkg { + my($self)=@_; + qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } ); +} + +=item cust_credit + +Returns a list consisting of the total previous credited (see +L) for this customer, followed by the previous outstanding +credits (FS::cust_credit objects). + +=cut + +sub cust_credit { + my($self)=@_; + my($total)=0; + my(@cust_credit) = sort { $a->_date <=> $b->date } + grep { $_->credited != 0 && $_->_date < $self->_date } + qsearch('cust_credit', { 'custnum' => $self->custnum } ) + ; + foreach (@cust_credit) { $total += $_->credited; } + $total, @cust_credit; +} + +=item cust_pay + +Returns all payments (see L) for this invoice. + +=cut + +sub cust_pay { + my($self)=@_; + sort { $a->_date <=> $b->date } + qsearch( 'cust_pay', { 'invnum' => $self->invnum } ) + ; +} + +=item print_text [TIME]; + +Returns an ASCII invoice, as a list of lines. + +TIME an optional value used to control the printing of overdue messages. The +default is now. It isn't the date of the invoice; that's the `_date' field. +It is specified as a UNIX timestamp; see L. Also see +L and L for conversion functions. + +=cut + +sub print_text { + + my($self,$today)=@_; + $today ||= time; + my($invnum)=$self->invnum; + my($cust_main) = qsearchs('cust_main', + { 'custnum', $self->custnum } ); + $cust_main->setfield('payname', + $cust_main->first. ' '. $cust_main->getfield('last') + ) unless $cust_main->payname; + + my($pr_total,@pr_cust_bill) = $self->previous; #previous balance + my($cr_total,@cr_cust_credit) = $self->cust_credit; #credits + my($balance_due) = $self->owed + $pr_total - $cr_total; + + #overdue? + my($overdue) = ( + $balance_due > 0 + && $today > $self->_date + && $self->printed > 1 + ); + + #printing bits here + + local($SIG{CHLD}) = sub { wait() }; + $|=1; + my($pid)=open(CHILD,"-|"); + die "Can't fork: $!" unless defined($pid); + + if ($pid) { #parent + my(@collect)=; + close CHILD; + return @collect; + } else { #child + + my($description,$amount); + my(@buf); + + #define format stuff + $%=0; + $= = 35; + local($^L) = <company if $cust_main->company; + $address[$l++]=$cust_main->address1; + $address[$l++]=$cust_main->address2 if $cust_main->address2; + $address[$l++]=$cust_main->city. ", ". $cust_main->state. " ". + $cust_main->zip; + $address[$l++]=$cust_main->country unless $cust_main->country eq 'US'; + + #previous balance + foreach ( @pr_cust_bill ) { + push @buf, ( + "Previous Balance, Invoice #". $_->invnum. + " (". time2str("%x",$_->_date). ")", + '$'. sprintf("%10.2f",$_->owed) + ); + } + if (@pr_cust_bill) { + push @buf,('','-----------'); + push @buf,('Total Previous Balance','$' . sprintf("%10.2f",$pr_total ) ); + push @buf,('',''); + } + + #new charges + foreach ( $self->cust_bill_pkg ) { + + if ( $_->pkgnum ) { + + my($cust_pkg)=qsearchs('cust_pkg', { 'pkgnum', $_->pkgnum } ); + my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->pkgpart}); + my($pkg)=$part_pkg->pkg; + + push @buf, ( "$pkg Setup",'$' . sprintf("%10.2f",$_->setup) ) + if $_->setup != 0; + push @buf, ( + "$pkg (" . time2str("%x",$_->sdate) . " - " . + time2str("%x",$_->edate) . ")", + '$' . sprintf("%10.2f",$_->recur) + ) if $_->recur != 0; + + } else { #pkgnum Tax + push @buf,("Tax",'$' . sprintf("%10.2f",$_->setup) ) + if $_->setup != 0; + } + } + + push @buf,('','-----------'); + push @buf,('Total New Charges', + '$' . sprintf("%10.2f",$self->charged) ); + push @buf,('',''); + + push @buf,('','-----------'); + push @buf,('Total Charges', + '$' . sprintf("%10.2f",$self->charged + $pr_total) ); + push @buf,('',''); + + #credits + foreach ( @cr_cust_credit ) { + push @buf,( + "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")", + '$' . sprintf("%10.2f",$_->credited) + ); + } + + #get & print payments + foreach ( $self->cust_pay ) { + push @buf,( + "Payment received ". time2str("%x",$_->_date ), + '$' . sprintf("%10.2f",$_->paid ) + ); + } + + #balance due + push @buf,('','-----------'); + push @buf,('Balance Due','$' . + sprintf("%10.2f",$self->owed + $pr_total - $cr_total ) ); + + #now print + + my($tot_pages)=int(scalar(@buf)/30); #15 lines, 2 values per line + $tot_pages++ if scalar(@buf) % 30; + + while (@buf) { + $description=shift(@buf); + $amount=shift(@buf); + write; + } + ($description,$amount)=('',''); + write while ( $- ); + print $^L; + + exit; #kid + + format STDOUT_TOP = + + @||||||||||||||||||| + "Invoice" + @||||||||||||||||||| @<<<<<<< @<<<<<<<<<<< +{ + ( $tot_pages != 1 ) ? "Page $% of $tot_pages" : '', + time2str("%x",( $self->_date )), "FS-$invnum" +} + + +@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +$add1 +@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +$add2 +@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +$add3 +@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +$add4 + + @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +{ $cust_main->payname, + ( ( $cust_main->payby eq 'BILL' ) && $cust_main->payinfo ) + ? "P.O. #". $cust_main->payinfo : '' +} + @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$address[0],'' + @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$address[1],$overdue ? "* This invoice is now PAST DUE! *" : '' + @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$address[2],$overdue ? " Please forward payment promptly " : '' + @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$address[3],$overdue ? "to avoid interruption of service." : '' + @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$address[4],'' + + + +. + + format STDOUT = + @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<< + $description,$amount +. + + } #endchild + +} + +=back + +=head1 BUGS + +The delete method. + +It doesn't properly override FS::Record yet. + +print_text formatting (and some logic :/) is in source as a format declaration, +which needs to be slurped in from a file. the fork is rather kludgy as well. +It could be cleaned with swrite from man perlform, and the picture could be +put in a /var/spool/freeside/conf file. Also number of lines ($=). + +missing print_ps for a nice postscript copy (maybe HylaFAX-cover-page-style +or something similar so the look can be completely customized?) + +There is an off-by-one error in print_text which causes a visual error: "Page 1 +of 2" printed on some single-page invoices? + +=head1 SEE ALSO + +L, L, L, L, +L, schema.html from the base documentation. + +=head1 HISTORY + +ivan@voicenet.com 97-jul-1 + +small fix for new API ivan@sisd.com 98-mar-14 + +charges can be negative ivan@sisd.com 98-jul-13 + +pod, ingegrate with FS::Invoice ivan@sisd.com 98-sep-20 + +=cut + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 2255eb267a486f9b6e6dea7b0a3bc5ce6a1d22d1 Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 22 Sep 1998 03:40:58 +0000 Subject: Initial revision --- site_perl/cust_bill_pkg.pm | 177 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 site_perl/cust_bill_pkg.pm diff --git a/site_perl/cust_bill_pkg.pm b/site_perl/cust_bill_pkg.pm new file mode 100644 index 000000000..e41d7c12c --- /dev/null +++ b/site_perl/cust_bill_pkg.pm @@ -0,0 +1,177 @@ +package FS::cust_bill_pkg; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields qsearchs); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +=head1 NAME + +FS::cust_bill_pkg - Object methods for cust_bill_pkg records + +=head1 SYNOPSIS + + use FS::cust_bill_pkg; + + $record = create FS::cust_bill_pkg \%hash; + $record = create FS::cust_bill_pkg { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_bill_pkg object represents an invoice line item. +FS::cust_bill_pkg inherits from FS::Record. The following fields are currently +supported: + +=over 4 + +=item invnum - invoice (see L) + +=item pkgnum - package (see L) + +=item setup - setup fee + +=item recur - recurring fee + +=item sdate - starting date of recurring fee + +=item edate - ending date of recurring fee + +=back + +sdate and edate are specified as UNIX timestamps; see L. Also +see L and L for conversion functions. + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new line item. To add the line item to the database, see +L<"insert">. Line items are normally created by calling the bill method of a +customer object (see L). + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('cust_bill_pkg')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('cust_bill_pkg',$hashref); + +} + +=item insert + +Adds this line item to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. I don't remove line items because there would then be +no record the items ever existed (which is bad, no?) + +=cut + +sub delete { + return "Can't delete cust_bill_pkg records!"; + #my($self)=@_; + #$self->del; +} + +=item replace OLD_RECORD + +Currently unimplemented. This would be even more of an accounting nightmare +than deleteing the items. Just don't do it. + +=cut + +sub replace { + return "Can't modify cust_bill_pkg records!"; + #my($new,$old)=@_; + #return "(Old) Not a cust_bill_pkg record!" + # unless $old->table eq "cust_bill_pkg"; + # + #$new->check or + #$new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid line item. If there is an +error, returns the error, otherwise returns false. Called by the insert +method. + +=cut + +sub check { + my($self)=@_; + return "Not a cust_bill_pkg record!" unless $self->table eq "cust_bill_pkg"; + + my($error)= + $self->ut_number('pkgnum') + or $self->ut_number('invnum') + or $self->ut_money('setup') + or $self->ut_money('recur') + or $self->ut_numbern('sdate') + or $self->ut_numbern('edate') + ; + return $error if $error; + + if ( $self->pkgnum != 0 ) { #allow unchecked pkgnum 0 for tax! (add to part_pkg?) + return "Unknown pkgnum ".$self->pkgnum + unless qsearchs('cust_pkg',{'pkgnum'=> $self->pkgnum }); + } + + return "Unknown invnum" + unless qsearchs('cust_bill',{'invnum'=> $self->invnum }); + + ''; #no error +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +=head1 SEE ALSO + +L, L, L, L, schema.html +from the base documentation. + +=head1 HISTORY + +ivan@sisd.com 98-mar-13 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 57ec0f19ef5202f425f9ad2c459069fb3b88e28a Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 22 Sep 1998 06:08:28 +0000 Subject: Initial revision --- site_perl/cust_main.pm | 868 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 868 insertions(+) create mode 100644 site_perl/cust_main.pm diff --git a/site_perl/cust_main.pm b/site_perl/cust_main.pm new file mode 100644 index 000000000..ec282731e --- /dev/null +++ b/site_perl/cust_main.pm @@ -0,0 +1,868 @@ +#this is so kludgy i'd be embarassed if it wasn't cybercash's fault +package main; +use vars qw($paymentserversecret $paymentserverport $paymentserverhost); + +package FS::cust_main; + +use strict; +use vars qw(@ISA @EXPORT_OK $conf $lpr $processor $xaction $E_NoErr); +use Safe; +use Exporter; +use Carp; +use Time::Local; +use Date::Format; +use Date::Manip; +use Business::CreditCard; +use FS::UID qw(getotaker); +use FS::Record qw(fields hfields qsearchs qsearch); +use FS::cust_pkg; +use FS::cust_bill; +use FS::cust_bill_pkg; +use FS::cust_pay; +#use FS::cust_pay_batch; + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(hfields); + +$conf = new FS::Conf; +$lpr = $conf->config('lpr'); + +if ( $conf->exists('cybercash3.2') ) { + require CCMckLib3_2; + #qw($MCKversion %Config InitConfig CCError CCDebug CCDebug2); + require CCMckDirectLib3_2; + #qw(SendCC2_1Server); + require CCMckErrno3_2; + #qw(MCKGetErrorMessage $E_NoErr); + import CCMckErrno3_2 qw($E_NoErr); + my $merchant_conf; + ($merchant_conf,$xaction)= $conf->config('cybercash3.2'); + my $status = &CCMckLib3_2::InitConfig($merchant_conf); + if ( $status != $E_NoErr ) { + warn "CCMckLib3_2::InitConfig error:\n"; + foreach my $key (keys %CCMckLib3_2::Config) { + warn " $key => $CCMckLib3_2::Config{$key}\n" + } + my($errmsg) = &CCMckErrno3_2::MCKGetErrorMessage($status); + die "CCMckLib3_2::InitConfig fatal error: $errmsg\n"; + } + $processor='cybercash3.2'; +} elsif ( $conf->exists('cybercash2') ) { + require CCLib; + #qw(sendmserver); + ( $main::paymentserverhost, + $main::paymentserverport, + $main::paymentserversecret, + $xaction, + ) = $conf->config('cybercash2'); + $processor='cybercash2'; +} + +=head1 NAME + +FS::cust_main - Object methods for cust_main records + +=head1 SYNOPSIS + + use FS::cust_main; + + $record = create FS::cust_main \%hash; + $record = create FS::cust_main { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + + @cust_pkg = $record->all_pkgs; + + @cust_pkg = $record->ncancelled_pkgs; + + $error = $record->bill; + $error = $record->bill %options; + $error = $record->bill 'time' => $time; + + $error = $record->collect; + $error = $record->collect %options; + $error = $record->collect 'invoice_time' => $time, + 'batch_card' => 'yes', + 'report_badcard' => 'yes', + ; + +=head1 DESCRIPTION + +An FS::cust_main object represents a customer. FS::cust_main inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item custnum - primary key (assigned automatically for new customers) + +=item agentnum - agent (see L) + +=item refnum - referral (see L) + +=item first - name + +=item last - name + +=item ss - social security number (optional) + +=item company - (optional) + +=item address1 + +=item address2 - (optional) + +=item city + +=item county - (optional, see L) + +=item state - (see L) + +=item zip + +=item country - (see L) + +=item daytime - phone (optional) + +=item night - phone (optional) + +=item payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free) + +=item payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username) + +=item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy + +=item payname - name on card or billing name + +=item tax - tax exempt, empty or `Y' + +=item otaker - order taker (assigned automatically, see L) + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new customer. To add the customer to the database, see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I method. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my $field; + #foreach $field (fields('cust_main')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('cust_main',$hashref); +} + +=item insert + +Adds this customer to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + #no callbacks in check, only data checks + #local $SIG{HUP} = 'IGNORE'; + #local $SIG{INT} = 'IGNORE'; + #local $SIG{QUIT} = 'IGNORE'; + #local $SIG{TERM} = 'IGNORE'; + #local $SIG{TSTP} = 'IGNORE'; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. Maybe cancel all of this customer's +packages (cust_pkg)? + +I don't remove the customer record in the database because there would then +be no record the customer ever existed (which is bad, no?) + +=cut + +# Usage: $error = $record -> delete; +sub delete { + return "Can't (yet?) delete customers."; +# my($self)=@_; +# +# $self->del; +} + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a cust_main record!" unless $old->table eq "cust_main"; + return "Can't change custnum!" + unless $old->getfield('custnum') eq $new->getfield('custnum'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid customer record. If there is +an error, returns the error, otherwise returns false. Called by the insert +and repalce methods. + +=cut + +sub check { + my($self)=@_; + + return "Not a cust_main record!" unless $self->table eq "cust_main"; + + my $error = + $self->ut_number('agentnum') + || $self->ut_number('refnum') + || $self->ut_textn('company') + || $self->ut_text('address1') + || $self->ut_textn('address2') + || $self->ut_text('city') + || $self->ut_textn('county') + || $self->ut_text('state') + || $self->ut_phonen('daytime') + || $self->ut_phonen('night') + || $self->ut_phonen('fax') + ; + return $error if $error; + + return "Unknown agent" + unless qsearchs('agent',{'agentnum'=>$self->agentnum}); + + return "Unknown referral" + unless qsearchs('part_referral',{'refnum'=>$self->refnum}); + + $self->getfield('last') =~ /^([\w \,\.\-\']+)$/ or return "Illegal last name"; + $self->setfield('last',$1); + + $self->first =~ /^([\w \,\.\-\']+)$/ or return "Illegal first name"; + $self->first($1); + + if ( $self->ss eq '' ) { + $self->ss(''); + } else { + my $ss = $self->ss; + $ss =~ s/\D//g; + $ss =~ /^(\d{3})(\d{2})(\d{4})$/ + or return "Illegal social security number"; + $self->ss("$1-$2-$3"); + } + + return "Unknown state/county/country" + unless qsearchs('cust_main_county',{ + 'state' => $self->state, + 'county' => $self->county, + } ); + + #int'l zips? + $self->zip =~ /^(\d{5}(-\d{4})?)$/ or return "Illegal zip"; + $self->zip($1); + + #int'l countries! + $self->country =~ /^(US)$/ or return "Illegal country"; + $self->country($1); + + $self->payby =~ /^(CARD|BILL|COMP)$/ or return "Illegal payby"; + $self->payby($1); + + if ( $self->payby eq 'CARD' ) { + + my $payinfo = $self->payinfo; + $payinfo =~ s/\D//g; + $payinfo =~ /^(\d{13,16})$/ + or return "Illegal credit card number"; + $payinfo = $1; + $self->payinfo($payinfo); + validate($payinfo) or return "Illegal credit card number"; + my $type = cardtype($payinfo); + return "Unknown credit card type" + unless ( $type =~ /^VISA/ || + $type =~ /^MasterCard/ || + $type =~ /^American Express/ || + $type =~ /^Discover/ ); + + } elsif ( $self->payby eq 'BILL' ) { + + $self->payinfo =~ /^([\w \-]*)$/ or return "Illegal P.O. number"; + $self->payinfo($1); + + } elsif ( $self->payby eq 'COMP' ) { + + $self->payinfo =~ /^(\w{2,8})$/ or return "Illegal comp account issuer"; + $self->payinfo($1); + + } + + if ( $self->paydate eq '' ) { + return "Expriation date required" unless $self->payby eq 'BILL'; + $self->paydate(''); + } else { + $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/ + or return "Illegal expiration date"; + if ( length($2) == 4 ) { + $self->paydate("$2-$1-01"); + } elsif ( $2 > 97 ) { #should pry change to check for "this year" + $self->paydate("19$2-$1-01"); + } else { + $self->paydate("20$2-$1-01"); + } + } + + if ( $self->payname eq '' ) { + $self->payname( $self->first. " ". $self->getfield('last') ); + } else { + $self->payname =~ /^([\w \,\.\-\']+)$/ + or return "Illegal billing name"; + $self->payname($1); + } + + $self->tax =~ /^(Y?)$/ or return "Illegal tax"; + $self->tax($1); + + $self->otaker(getotaker); + + ''; #no error +} + +=item all_pkgs + +Returns all packages (see L) for this customer. + +=cut + +sub all_pkgs { + my($self)=@_; + qsearch( 'cust_pkg', { 'custnum' => $self->custnum }); +} + +=item ncancelled_pkgs + +Returns all non-cancelled packages (see L) for this customer. + +=cut + +sub ncancelled_pkgs { + my($self)=@_; + qsearch( 'cust_pkg', { + 'custnum' => $self->custnum, + 'cancel' => '', + }); +} + +=item bill OPTIONS + +Generates invoices (see L) for this customer. Usually used in +conjunction with the collect method. + +The only currently available option is `time', which bills the customer as if +it were that time. It is specified as a UNIX timestamp; see +L). Also see L and L for conversion +functions. + +If there is an error, returns the error, otherwise returns false. + +=cut + +sub bill { + my($self,%options)=@_; + my($time) = $options{'time'} || $^T; + + my($error); + + #put below somehow? + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + # find the packages which are due for billing, find out how much they are + # & generate invoice database. + + my($total_setup,$total_recur)=(0,0); + + my(@cust_bill_pkg); + + my($cust_pkg); + foreach $cust_pkg ( + qsearch('cust_pkg',{'custnum'=> $self->getfield('custnum') } ) + ) { + + bless($cust_pkg,"FS::cust_pkg"); + + next if ( $cust_pkg->getfield('cancel') ); + + #? to avoid use of uninitialized value errors... ? + $cust_pkg->setfield('bill', '') + unless defined($cust_pkg->bill); + + my($part_pkg)= + qsearchs('part_pkg',{'pkgpart'=> $cust_pkg->pkgpart } ); + + #so we don't modify cust_pkg record unnecessarily + my($cust_pkg_mod_flag)=0; + my(%hash)=$cust_pkg->hash; + my($old_cust_pkg)=create FS::cust_pkg(\%hash); + + # bill setup + my($setup)=0; + unless ( $cust_pkg->setup ) { + my($setup_prog)=$part_pkg->getfield('setup'); + my($cpt) = new Safe; + #$cpt->permit(); #what is necessary? + $cpt->share(qw($cust_pkg)); #can $cpt now use $cust_pkg methods? + $setup = $cpt->reval($setup_prog); + unless ( defined($setup) ) { + warn "Error reval-ing part_pkg->setup pkgpart ", + $part_pkg->pkgpart, ": $@"; + } else { + $cust_pkg->setfield('setup',$time); + $cust_pkg_mod_flag=1; + } + } + + #bill recurring fee + my($recur)=0; + my($sdate); + if ( $part_pkg->getfield('freq') > 0 && + ! $cust_pkg->getfield('susp') && + ( $cust_pkg->getfield('bill') || 0 ) < $time + ) { + my($recur_prog)=$part_pkg->getfield('recur'); + my($cpt) = new Safe; + #$cpt->permit(); #what is necessary? + $cpt->share(qw($cust_pkg)); #can $cpt now use $cust_pkg methods? + $recur = $cpt->reval($recur_prog); + unless ( defined($recur) ) { + warn "Error reval-ing part_pkg->recur pkgpart ", + $part_pkg->pkgpart, ": $@"; + } else { + #change this bit to use Date::Manip? + #$sdate=$cust_pkg->bill || time; + #$sdate=$cust_pkg->bill || $time; + $sdate=$cust_pkg->bill || $cust_pkg->setup || $time; + my($sec,$min,$hour,$mday,$mon,$year)= + (localtime($sdate) )[0,1,2,3,4,5]; + $mon += $part_pkg->getfield('freq'); + until ( $mon < 12 ) { $mon -= 12; $year++; } + $cust_pkg->setfield('bill',timelocal($sec,$min,$hour,$mday,$mon,$year)); + $cust_pkg_mod_flag=1; + } + } + + warn "setup is undefinded" unless defined($setup); + warn "recur is undefinded" unless defined($recur); + warn "cust_pkg bill is undefinded" unless defined($cust_pkg->bill); + + if ($cust_pkg_mod_flag) { + $error=$cust_pkg->replace($old_cust_pkg); + if ( $error ) { + warn "Error modifying pkgnum ", $cust_pkg->pkgnum, ": $error"; + } else { + #just in case + $setup=sprintf("%.2f",$setup); + $recur=sprintf("%.2f",$recur); + my($cust_bill_pkg)=create FS::cust_bill_pkg ({ + 'pkgnum' => $cust_pkg->pkgnum, + 'setup' => $setup, + 'recur' => $recur, + 'sdate' => $sdate, + 'edate' => $cust_pkg->bill, + }); + push @cust_bill_pkg, $cust_bill_pkg; + $total_setup += $setup; + $total_recur += $recur; + } + } + + } + + my($charged)=sprintf("%.2f",$total_setup + $total_recur); + + return '' if scalar(@cust_bill_pkg) == 0; + + unless ( $self->getfield('tax') eq 'Y' || + $self->getfield('tax') eq 'y' || + $self->getfield('payby') eq 'COMP' + ) { + my($cust_main_county) = qsearchs('cust_main_county',{ + 'county' => $self->getfield('county'), + 'state' => $self->getfield('state'), + } ); + my($tax) = sprintf("%.2f", + $charged * ( $cust_main_county->getfield('tax') / 100 ) + ); + $charged = sprintf("%.2f",$charged+$tax); + + my($cust_bill_pkg)=create FS::cust_bill_pkg ({ + 'pkgnum' => 0, + 'setup' => $tax, + 'recur' => 0, + 'sdate' => '', + 'edate' => '', + }); + push @cust_bill_pkg, $cust_bill_pkg; + } + + my($cust_bill) = create FS::cust_bill ( { + 'custnum' => $self->getfield('custnum'), + '_date' => $time, + 'charged' => $charged, + } ); + $error=$cust_bill->insert; + #shouldn't happen, but how else to handle this? (wrap me in eval, to catch + # fatal errors) + die "Error creating cust_bill record: $error!\n", + "Check updated but unbilled packages for customer", $self->custnum, "\n" + if $error; + + my($invnum)=$cust_bill->invnum; + my($cust_bill_pkg); + foreach $cust_bill_pkg ( @cust_bill_pkg ) { + $cust_bill_pkg->setfield('invnum',$invnum); + $error=$cust_bill_pkg->insert; + #shouldn't happen, but how else tohandle this? + die "Error creating cust_bill_pkg record: $error!\n", + "Check incomplete invoice ", $invnum, "\n" + if $error; + } + + ''; #no error +} + +=item collect OPTIONS + +(Attempt to) collect money for this customer's outstanding invoices (see +L). Usually used after the bill method. + +Depending on the value of `payby', this may print an invoice (`BILL'), charge +a credit card (`CARD'), or just add any necessary (pseudo-)payment (`COMP'). + +If there is an error, returns the error, otherwise returns false. + +Currently available options are: + +invoice_time - Use this time when deciding when to print invoices and +late notices on those invoices. The default is now. It is specified as a UNIX timestamp; see L). Also see L and L +for conversion functions. + +batch_card - Set this true to batch cards (see L). By +default, cards are processed immediately, which will generate an error if +CyberCash is not installed. + +report_badcard - Set this true if you want bad card transactions to +return an error. By default, they don't. + +=cut + +sub collect { + my($self,%options)=@_; + my($invoice_time) = $options{'invoice_time'} || $^T; + + my($total_owed) = $self->balance; + return '' unless $total_owed > 0; #redundant????? + + #put below somehow? + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + foreach my $cust_bill ( qsearch('cust_bill', { + 'custnum' => $self->getfield('custnum'), + } ) ) { + + #this has to be before next's + my($amount) = sprintf("%.2f", $total_owed < $cust_bill->owed + ? $total_owed + : $cust_bill->owed + ); + $total_owed = sprintf("%.2f",$total_owed-$amount); + + next unless $cust_bill->owed > 0; + + next if qsearchs('cust_pay_batch',{'invnum'=> $cust_bill->invnum }); + + #warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ", amount $amount, total_owed $total_owed)"; + + next unless $amount > 0; + + if ( $self->getfield('payby') eq 'BILL' ) { + + #30 days 2592000 + my($since)=$invoice_time - ( $cust_bill->_date || 0 ); + #warn "$invoice_time ", $cust_bill->_date, " $since"; + if ( $since >= 0 #don't print future invoices + && ( $cust_bill->printed * 2592000 ) <= $since + ) { + + open(LPR,$lpr) or die "Can't open $lpr: $!"; + print LPR $cust_bill->print_text; #( date ) + close LPR + or die $! ? "Error closing $lpr: $!" + : "Exit status $? from $lpr"; + + my(%hash)=$cust_bill->hash; + $hash{'printed'}++; + my($new_cust_bill)=create FS::cust_bill(\%hash); + my($error)=$new_cust_bill->replace($cust_bill); + if ( $error ) { + warn "Error updating $cust_bill->printed: $error"; + } + + } + + } elsif ( $self->getfield('payby') eq 'COMP' ) { + my($cust_pay) = create FS::cust_pay ( { + 'invnum' => $cust_bill->getfield('invnum'), + 'paid' => $amount, + '_date' => '', + 'payby' => 'COMP', + 'payinfo' => $self->getfield('payinfo'), + 'paybatch' => '' + } ); + my($error)=$cust_pay->insert; + return 'Error COMPing invnum #' . $cust_bill->getfield('invnum') . + ':' . $error if $error; + } elsif ( $self->getfield('payby') eq 'CARD' ) { + + if ( $options{'batch_card'} ne 'yes' ) { + + return "Real time card processing not enabled!" unless $processor; + + if ( $processor =~ /cybercash/ ) { + + #fix exp. date for cybercash + $self->getfield('paydate') =~ /^(\d+)\/\d*(\d{2})$/; + my($exp)="$1/$2"; + + my($paybatch)= $cust_bill->getfield('invnum') . + '-' . time2str("%y%m%d%H%M%S",time); + + my($payname)= $self->getfield('payname') || + $self->getfield('first') . ' ' .$self->getfield('last'); + + my($address)= $self->getfield('address1'); + $address .= ", " . $self->getfield('address2') + if $self->getfield('address2'); + + my($country) = $self->getfield('country') eq 'US' ? + 'USA' : $self->getfield('country'); + + my(@full_xaction)=($xaction, + 'Order-ID' => $paybatch, + 'Amount' => "usd $amount", + 'Card-Number' => $self->getfield('payinfo'), + 'Card-Name' => $payname, + 'Card-Address' => $address, + 'Card-City' => $self->getfield('city'), + 'Card-State' => $self->getfield('state'), + 'Card-Zip' => $self->getfield('zip'), + 'Card-Country' => $country, + 'Card-Exp' => $exp, + ); + + my(%result); + if ( $processor eq 'cybercash2' ) { + $^W=0; #CCLib isn't -w safe, ugh! + %result = &CCLib::sendmserver(@full_xaction); + $^W=1; + } elsif ( $processor eq 'cybercash3.2' ) { + %result = &CCMckDirectLib3_2::SendCC2_1Server(@full_xaction); + } else { + return "Unkonwn real-time processor $processor\n"; + } + + #if ( $result{'MActionCode'} == 7 ) { #cybercash smps v.1.1.3 + #if ( $result{'action-code'} == 7 ) { #cybercash smps v.2.1 + if ( $result{'MStatus'} eq 'success' ) { #cybercash smps v.2 or 3 + my($cust_pay) = create FS::cust_pay ( { + 'invnum' => $cust_bill->getfield('invnum'), + 'paid' => $amount, + '_date' => '', + 'payby' => 'CARD', + 'payinfo' => $self->getfield('payinfo'), + 'paybatch' => "$processor:$paybatch", + } ); + my($error)=$cust_pay->insert; + return 'Error applying payment, invnum #' . + $cust_bill->getfield('invnum') . ':' . $error if $error; + } elsif ( $result{'Mstatus'} ne 'failure-bad-money' + || $options{'report_badcard'} ) { + return 'Cybercash error, invnum #' . + $cust_bill->getfield('invnum') . ':' . $result{'MErrMsg'}; + } else { + return ''; + } + + } else { + return "Unkonwn real-time processor $processor\n"; + } + + } else { #batch card + +# my($cust_pay_batch) = create FS::cust_pay_batch ( { + my($cust_pay_batch) = new FS::Record ('cust_pay_batch', { + 'invnum' => $cust_bill->getfield('invnum'), + 'custnum' => $self->getfield('custnum'), + 'last' => $self->getfield('last'), + 'first' => $self->getfield('first'), + 'address1' => $self->getfield('address1'), + 'address2' => $self->getfield('address2'), + 'city' => $self->getfield('city'), + 'state' => $self->getfield('state'), + 'zip' => $self->getfield('zip'), + 'country' => $self->getfield('country'), + 'trancode' => 77, + 'cardnum' => $self->getfield('payinfo'), + 'exp' => $self->getfield('paydate'), + 'payname' => $self->getfield('payname'), + 'amount' => $amount, + } ); +# my($error)=$cust_pay_batch->insert; + my($error)=$cust_pay_batch->add; + return "Error adding to cust_pay_batch: $error" if $error; + + } + + } else { + return "Unknown payment type ".$self->getfield('payby'); + } + + } + ''; + +} + +=item total_owed + +Returns the total owed for this customer on all invoices +(see L). + +=cut + +sub total_owed { + my($self) = @_; + my($total_bill) = 0; + my($cust_bill); + foreach $cust_bill ( qsearch('cust_bill', { + 'custnum' => $self->getfield('custnum'), + } ) ) { + $total_bill += $cust_bill->getfield('owed'); + } + sprintf("%.2f",$total_bill); +} + +=item total_credited + +Returns the total credits (see L) for this customer. + +=cut + +sub total_credited { + my($self) = @_; + my($total_credit) = 0; + my($cust_credit); + foreach $cust_credit ( qsearch('cust_credit', { + 'custnum' => $self->getfield('custnum'), + } ) ) { + $total_credit += $cust_credit->getfield('credited'); + } + sprintf("%.2f",$total_credit); +} + +=item balance + +Returns the balance for this customer (total owed minus total credited). + +=cut + +sub balance { + my($self) = @_; + sprintf("%.2f",$self->total_bill - $self->total_credit); +} + +=back + +=head1 BUGS + +The delete method. + +It doesn't properly override FS::Record yet. + +hfields should be removed. + +Bill and collect options should probably be passed as references instead of a +list. + +CyberCash v2 forces us to define some variables in package main. + +=head1 SEE ALSO + +L, L, L, L +L, L, L, +L, L, schema.html from the base documentation. + +=head1 HISTORY + +ivan@voicenet.com 97-jul-28 + +Changed to standard Business::CreditCard +no more TableUtil +EXPORT_OK FS::Record's hfields +removed unique calls and locking (not needed here now) +wrapped the (now) optional fields in if statements in sub check (notyetdone!) +ivan@sisd.com 97-nov-12 + +updated paydate with SQL-type date info ivan@sisd.com 98-mar-5 + +Added export of datasrc from UID.pm for Pg6.3 +changed 'day' to 'daytime' because Pg6.3 reserves the day word + bmccane@maxbaud.net 98-apr-3 + +in ->create, s/svc_acct/cust_main/, now it should actually eliminate the +warnings it was meant to ivan@sisd.com 98-jul-16 + +don't require a phone number and allow '/' in company names +ivan@sisd.com 98-jul-18 + +use ut_ and rewrite &check, &*_pkgs ivan@sisd.com 98-sep-5 + +pod, merge with FS::Bill (about time!), total_owed, total_credited and balance +methods, cleaned collect method, source modifications no longer necessary to +enable cybercash, cybercash v3 support, don't need to import +FS::UID::{datasrc,checkruid} ivan@sisd.com 98-sep-19-21 + +=cut + +1; + + -- cgit v1.2.1 -- cgit v1.2.1 From 762e8ec0ead5dc8c8978d93695adbcc1265ca83a Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 23 Sep 1998 07:27:04 +0000 Subject: Initial revision --- site_perl/agent_type.pm | 161 +++++++++++++++++++++++++++++ site_perl/cust_credit.pm | 199 +++++++++++++++++++++++++++++++++++ site_perl/cust_main_county.pm | 161 +++++++++++++++++++++++++++++ site_perl/cust_pay.pm | 235 ++++++++++++++++++++++++++++++++++++++++++ site_perl/cust_svc.pm | 168 ++++++++++++++++++++++++++++++ site_perl/part_pkg.pm | 168 ++++++++++++++++++++++++++++++ site_perl/part_referral.pm | 155 ++++++++++++++++++++++++++++ site_perl/pkg_svc.pm | 168 ++++++++++++++++++++++++++++++ 8 files changed, 1415 insertions(+) create mode 100644 site_perl/agent_type.pm create mode 100644 site_perl/cust_credit.pm create mode 100644 site_perl/cust_main_county.pm create mode 100644 site_perl/cust_pay.pm create mode 100644 site_perl/cust_svc.pm create mode 100644 site_perl/part_pkg.pm create mode 100644 site_perl/part_referral.pm create mode 100644 site_perl/pkg_svc.pm diff --git a/site_perl/agent_type.pm b/site_perl/agent_type.pm new file mode 100644 index 000000000..002c36f54 --- /dev/null +++ b/site_perl/agent_type.pm @@ -0,0 +1,161 @@ +package FS::agent_type; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(qsearch fields); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +=head1 NAME + +FS::agent_type - Object methods for agent_type records + +=head1 SYNOPSIS + + use FS::agent_type; + + $record = create FS::agent_type \%hash; + $record = create FS::agent_type { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::agent_type object represents an agent type. Every agent (see +L) has an agent type. Agent types define which packages (see +L) may be purchased by customers (see L), via +FS::type_pkgs records (see L). FS::agent_type inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item typenum - primary key (assigned automatically for new agent types) + +=item atype - Text name of this agent type + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new agent type. To add the agent type to the database, see +L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('agent_type')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('agent_type',$hashref); + +} + +=item insert + +Adds this agent type to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Deletes this agent type from the database. Only agent types with no agents +can be deleted. If there is an error, returns the error, otherwise returns +false. + +=cut + +sub delete { + my($self)=@_; + return "Can't delete an agent_type with agents!" + if qsearch('agent',{'typenum' => $self->typenum}); + $self->del; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a agent_type record!" unless $old->table eq "agent_type"; + return "Can't change typenum!" + unless $old->getfield('typenum') eq $new->getfield('typenum'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid agent type. If there is an +error, returns the error, otherwise returns false. Called by the insert and +replace methods. + +=cut + +sub check { + my($self)=@_; + return "Not a agent_type record!" unless $self->table eq "agent_type"; + + $self->ut_numbern('typenum') + or $self->ut_text('atype'); + +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +=head1 SEE ALSO + +L, L, L, L, +L, schema.html from the base documentation. + +=head1 HISTORY + +Class for the different sets of allowable packages you can assign to an +agent. + +ivan@sisd.com 97-nov-13 + +ut_ FS::Record methods +ivan@sisd.com 97-dec-10 + +Changed 'type' to 'atype' because Pg6.3 reserves the type word + bmccane@maxbaud.net 98-apr-3 + +pod, added check in delete ivan@sisd.com 98-sep-21 + +=cut + +1; + diff --git a/site_perl/cust_credit.pm b/site_perl/cust_credit.pm new file mode 100644 index 000000000..b1a5e1649 --- /dev/null +++ b/site_perl/cust_credit.pm @@ -0,0 +1,199 @@ +package FS::cust_credit; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::UID qw(getotaker); +use FS::Record qw(fields qsearchs); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +=head1 NAME + +FS::cust_credit - Object methods for cust_credit records + +=head1 SYNOPSIS + + use FS::cust_credit; + + $record = create FS::cust_credit \%hash; + $record = create FS::cust_credit { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_credit object represents a credit. FS::cust_credit inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item crednum - primary key (assigned automatically for new credits) + +=item custnum - customer (see L) + +=item amount - amount of the credit + +=item credited - how much of this credit that is still outstanding, which is +amount minus all refunds (see L). + +=item _date - specified as a UNIX timestamp; see L. Also see +L and L for conversion functions. + +=item otaker - order taker (assigned automatically, see L) + +=item reason - text + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new credit. To add the credit to the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('cust_credit')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('cust_credit',$hashref); +} + +=item insert + +Adds this credit to the database ("Posts" the credit). If there is an error, +returns the error, otherwise returns false. + +When adding new invoices, credited must be amount (or null, in which case it is +automatically set to amount). + +=cut + +sub insert { + my($self)=@_; + + $self->setfield('credited',$self->amount) if $self->credited eq ''; + return "credited != amount!" + unless $self->credited == $self->amount; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. + +=cut + +sub delete { + return "Can't remove credit!" + #my($self)=@_; + #$self->del; +} + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +Only credited may be changed. Credited is normally updated by creating and +inserting a refund (see L). + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a cust_credit record!" unless $old->table eq "cust_credit"; + return "Can't change crednum!" + unless $old->getfield('crednum') eq $new->getfield('crednum'); + return "Can't change custnum!" + unless $old->getfield('custnum') eq $new->getfield('custnum'); + return "Can't change date!" + unless $old->getfield('_date') eq $new->getfield('_date'); + return "Can't change amount!" + unless $old->getfield('amount') eq $new->getfield('amount'); + return "(New) credited can't be > (new) amount!" + if $new->getfield('credited') > $new->getfield('amount'); + + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid credit. If there is an error, +returns the error, otherwise returns false. Called by the insert and replace +methods. + +=cut + +sub check { + my($self)=@_; + return "Not a cust_credit record!" unless $self->table eq "cust_credit"; + my($recref) = $self->hashref; + + $recref->{crednum} =~ /^(\d*)$/ or return "Illegal crednum"; + $recref->{crednum} = $1; + + $recref->{custnum} =~ /^(\d+)$/ or return "Illegal custnum"; + $recref->{custnum} = $1; + return "Unknown customer" + unless qsearchs('cust_main',{'custnum'=>$recref->{custnum}}); + + $recref->{_date} =~ /^(\d*)$/ or return "Illegal date"; + $recref->{_date} = $recref->{_date} ? $1 : time; + + $recref->{amount} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal amount"; + $recref->{amount} = $1; + + $recref->{credited} =~ /^(\-?\d+(\.\d\d)?)$/ or return "Illegal credited"; + $recref->{credited} = $1; + + #$recref->{otaker} =~ /^(\w+)$/ or return "Illegal otaker"; + #$recref->{otaker} = $1; + $self->otaker(getotaker); + + $self->ut_textn('reason'); + +} + +=back + +=head1 BUGS + +The delete method. + +It doesn't properly override FS::Record yet. + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=head1 HISTORY + +ivan@sisd.com 98-mar-17 + +pod, otaker from FS::UID ivan@sisd.com 98-sep-21 + +=cut + +1; + diff --git a/site_perl/cust_main_county.pm b/site_perl/cust_main_county.pm new file mode 100644 index 000000000..f4b4595ae --- /dev/null +++ b/site_perl/cust_main_county.pm @@ -0,0 +1,161 @@ +package FS::cust_main_county; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields hfields qsearch qsearchs); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(hfields); + +=head1 NAME + +FS::cust_main_county - Object methods for cust_main_county objects + +=head1 SYNOPSIS + + use FS::cust_main_county; + + $record = create FS::cust_main_county \%hash; + $record = create FS::cust_main_county { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_main_county object represents a tax rate, defined by locale. +FS::cust_main_county inherits from FS::Record. The following fields are +currently supported: + +=over 4 + +=item taxnum - primary key (assigned automatically for new tax rates) + +=item state + +=item county + +=item tax - percentage + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new tax rate. To add the tax rate to the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('cust_main_county')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('cust_main_county',$hashref); +} + +=item insert + +Adds this tax rate to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Deletes this tax rate from the database. If there is an error, returns the +error, otherwise returns false. + +=cut + +sub delete { + my($self)=@_; + + $self->del; +} + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a cust_main_county record!" + unless $old->table eq "cust_main_county"; + return "Can't change taxnum!" + unless $old->getfield('taxnum') eq $new->getfield('taxnum'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid tax rate. If there is an error, +returns the error, otherwise returns false. Called by the insert and replace +methods. + +=cut + +sub check { + my($self)=@_; + return "Not a cust_main_county record!" + unless $self->table eq "cust_main_county"; + my($recref) = $self->hashref; + + $self->ut_numbern('taxnum') + or $self->ut_text('state') + or $self->ut_textn('county') + or $self->ut_float('tax') + ; + +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +A country field (and possibly a currency field) should be added. + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=head1 HISTORY + +ivan@voicenet.com 97-dec-16 + +Changed check for 'tax' to use the new ut_float subroutine + bmccane@maxbaud.net 98-apr-3 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + diff --git a/site_perl/cust_pay.pm b/site_perl/cust_pay.pm new file mode 100644 index 000000000..6e30c595b --- /dev/null +++ b/site_perl/cust_pay.pm @@ -0,0 +1,235 @@ +package FS::cust_pay; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use Business::CreditCard; +use FS::Record qw(fields qsearchs); +use FS::cust_bill; + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +=head1 NAME + +FS::cust_pay - Object methods for cust_pay objects + +=head1 SYNOPSIS + + use FS::cust_pay; + + $record = create FS::cust_pay \%hash; + $record = create FS::cust_pay { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_pay object represents a payment. FS::cust_pay inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item paynum - primary key (assigned automatically for new payments) + +=item invnum - Invoice (see L) + +=item paid - Amount of this payment + +=item _date - specified as a UNIX timestamp; see L. Also see +L and L for conversion functions. + +=item payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free) + +=item payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username) + +=item paybatch - text field for tracking card processing + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new payment. To add the payment to the databse, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('cust_pay')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('cust_pay',$hashref); + +} + +=item insert + +Adds this payment to the databse, and updates the invoice (see +L). + +=cut + +sub insert { + my($self)=@_; + + my($error); + + $error=$self->check; + return $error if $error; + + my($old_cust_bill) = qsearchs('cust_bill', { + 'invnum' => $self->getfield('invnum') + } ); + return "Unknown invnum" unless $old_cust_bill; + my(%hash)=$old_cust_bill->hash; + $hash{owed} = sprintf("%.2f",$hash{owed} - $self->getfield('paid') ); + my($new_cust_bill) = create FS::cust_bill ( \%hash ); + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + $error=$new_cust_bill -> replace($old_cust_bill); + return "Error modifying cust_bill: $error" if $error; + + $self->add; +} + +=item delete + +Currently unimplemented (accounting reasons). + +=cut + +sub delete { + return "Can't (yet?) delete cust_pay records!"; +#template code below +# my($self)=@_; +# +# $self->del; +} + +=item replace OLD_RECORD + +Currently unimplemented (accounting reasons). + +=cut + +sub replace { + return "Can't (yet?) modify cust_pay records!"; +#template code below +# my($new,$old)=@_; +# return "(Old) Not a cust_pay record!" unless $old->table eq "cust_pay"; +# +# $new->check or +# $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid payment. If there is an error, +returns the error, otherwise returns false. Called by the insert method. + +=cut + +sub check { + my($self)=@_; + return "Not a cust_pay record!" unless $self->table eq "cust_pay"; + my($recref) = $self->hashref; + + $recref->{paynum} =~ /^(\d*)$/ or return "Illegal paynum"; + $recref->{paynum} = $1; + + $recref->{invnum} =~ /^(\d+)$/ or return "Illegal invnum"; + $recref->{invnum} = $1; + + $recref->{paid} =~ /^(\d+(\.\d\d)?)$/ or return "Illegal paid"; + $recref->{paid} = $1; + + $recref->{_date} =~ /^(\d*)$/ or return "Illegal date"; + $recref->{_date} = $recref->{_date} ? $1 : time; + + $recref->{payby} =~ /^(CARD|BILL|COMP)$/ or return "Illegal payby"; + $recref->{payby} = $1; + + if ( $recref->{payby} eq 'CARD' ) { + + $recref->{payinfo} =~ s/\D//g; + if ( $recref->{payinfo} ) { + $recref->{payinfo} =~ /^(\d{13,16})$/ + or return "Illegal (mistyped?) credit card number (payinfo)"; + $recref->{payinfo} = $1; + #validate($recref->{payinfo}) + # or return "Illegal credit card number"; + my($type)=cardtype($recref->{payinfo}); + return "Unknown credit card type" + unless ( $type =~ /^VISA/ || + $type =~ /^MasterCard/ || + $type =~ /^American Express/ || + $type =~ /^Discover/ ); + } else { + $recref->{payinfo}='N/A'; + } + + } elsif ( $recref->{payby} eq 'BILL' ) { + + $recref->{payinfo} =~ /^([\w \-]*)$/ + or return "Illegal P.O. number (payinfo)"; + $recref->{payinfo} = $1; + + } elsif ( $recref->{payby} eq 'COMP' ) { + + $recref->{payinfo} =~ /^([\w]{2,8})$/ + or return "Illegal comp account issuer (payinfo)"; + $recref->{payinfo} = $1; + + } + + $recref->{paybatch} =~ /^([\w\-\:]*)$/ + or return "Illegal paybatch"; + $recref->{paybatch} = $1; + + ''; #no error + +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +Delete and replace methods. + +=head1 SEE ALSO + +L, L, schema.html from the base documentation. + +=head1 HISTORY + +ivan@voicenet.com 97-jul-1 - 25 - 29 + +new api ivan@sisd.com 98-mar-13 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + diff --git a/site_perl/cust_svc.pm b/site_perl/cust_svc.pm new file mode 100644 index 000000000..1d5051b1f --- /dev/null +++ b/site_perl/cust_svc.pm @@ -0,0 +1,168 @@ +package FS::cust_svc; + +use strict; +use vars qw(@ISA); +use Exporter; +use FS::Record qw(fields qsearchs); + +@ISA = qw(FS::Record Exporter); + +=head1 NAME + +FS::cust_svc - Object method for cust_svc objects + +=head1 SYNOPSIS + + use FS::cust_svc; + + $record = create FS::cust_svc \%hash + $record = create FS::cust_svc { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::cust_svc represents a service. FS::cust_svc inherits from FS::Record. +The following fields are currently supported: + +=over 4 + +=item svcnum - primary key (assigned automatically for new services) + +=item pkgnum - Package (see L) + +=item svcpart - Service definition (see L) + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new service. To add the refund to the database, see L<"insert">. +Services are normally created by creating FS::svc_ objects (see +L, L, and L, among others). + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('cust_svc')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('cust_svc',$hashref); +} + +=item insert + +Adds this service to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Deletes this service from the database. If there is an error, returns the +error, otherwise returns false. + +Called by the cancel method of the package (see L). + +=cut + +sub delete { + my($self)=@_; + # anything else here? + $self->del; +} + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a cust_svc record!" unless $old->table eq "cust_svc"; + return "Can't change svcnum!" + unless $old->getfield('svcnum') eq $new->getfield('svcnum'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid service. If there is an error, +returns the error, otehrwise returns false. Called by the insert and +replace methods. + +=cut + +sub check { + my($self)=@_; + return "Not a cust_svc record!" unless $self->table eq "cust_svc"; + my($recref) = $self->hashref; + + $recref->{svcnum} =~ /^(\d*)$/ or return "Illegal svcnum"; + $recref->{svcnum}=$1; + + $recref->{pkgnum} =~ /^(\d*)$/ or return "Illegal pkgnum"; + $recref->{pkgnum}=$1; + return "Unknown pkgnum" unless + ! $recref->{pkgnum} || + qsearchs('cust_pkg',{'pkgnum'=>$recref->{pkgnum}}); + + $recref->{svcpart} =~ /^(\d+)$/ or return "Illegal svcpart"; + $recref->{svcpart}=$1; + return "Unknown svcpart" unless + qsearchs('part_svc',{'svcpart'=>$recref->{svcpart}}); + + ''; #no error +} + +=back + +=head1 BUGS + +Behaviour of changing the svcpart of cust_svc records is undefined and should +possibly be prohibited, and pkg_svc records are not checked. + +pkg_svc records are not checket in general (here). + +=head1 SEE ALSO + +L, L, L, L, +schema.html from the base documentation + +=head1 HISTORY + +ivan@voicenet.com 97-jul-10,14 + +no TableUtil, no FS::Lock ivan@sisd.com 98-mar-7 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + diff --git a/site_perl/part_pkg.pm b/site_perl/part_pkg.pm new file mode 100644 index 000000000..d1c12e47e --- /dev/null +++ b/site_perl/part_pkg.pm @@ -0,0 +1,168 @@ +package FS::part_pkg; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields hfields); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(hfields fields); + +=head1 NAME + +FS::part_pkg - Object methods for part_pkg objects + +=head1 SYNOPSIS + + use FS::part_pkg; + + $record = create FS::part_pkg \%hash + $record = create FS::part_pkg { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::part_pkg represents a billing item definition. FS::part_pkg inherits +from FS::Record. The following fields are currently supported: + +=over 4 + +=item pkgpart - primary key (assigned automatically for new billing item definitions) + +=item pkg - Text name of this billing item definition (customer-viewable) + +=item comment - Text name of this billing item definition (non-customer-viewable) + +=item setup - Setup fee + +=item freq - Frequency of recurring fee + +=item recur - Recurring fee + +=back + +setup and recur are evaluated as Safe perl expressions. You can use numbers +just as you would normally. More advanced semantics are not yet defined. + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new billing item definition. To add the billing item definition to +the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('part_pkg')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('part_pkg',$hashref); +} + +=item insert + +Adds this billing item definition to the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. + +=cut + +sub delete { + return "Can't (yet?) delete package definitions."; +# maybe check & make sure the pkgpart isn't in cust_pkg or type_pkgs? +# my($self)=@_; +# +# $self->del; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a part_pkg record!" unless $old->table eq "part_pkg"; + return "Can't change pkgpart!" + unless $old->getfield('pkgpart') eq $new->getfield('pkgpart'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid billing item definition. If +there is an error, returns the error, otherwise returns false. Called by the +insert and replace methods. + +=cut + +sub check { + my($self)=@_; + return "Not a part_pkg record!" unless $self->table eq "part_pkg"; + + $self->ut_numbern('pkgpart') + or $self->ut_text('pkg') + or $self->ut_text('comment') + or $self->ut_anything('setup') + or $self->ut_number('freq') + or $self->ut_anything('recur') + ; + +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +The delete method is unimplemented. + +setup and recur semantics are not yet defined (and are implemented in +FS::cust_bill. hmm.). + +=head1 SEE ALSO + +L, L, L, L, L. +schema.html from the base documentation. + +=head1 HISTORY + +ivan@sisd.com 97-dec-5 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + diff --git a/site_perl/part_referral.pm b/site_perl/part_referral.pm new file mode 100644 index 000000000..1b4a1b65a --- /dev/null +++ b/site_perl/part_referral.pm @@ -0,0 +1,155 @@ +package FS::part_referral; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields qsearchs); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +=head1 NAME + +FS::part_referral - Object methods for part_referral objects + +=head1 SYNOPSIS + + use FS::part_referral; + + $record = create FS::part_referral \%hash + $record = create FS::part_referral { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::part_referral represents a referral - where a customer heard of your +services. This can be used to track the effectiveness of a particular piece of +advertising, for example. FS::part_referral inherits from FS::Record. The +following fields are currently supported: + +=over 4 + +=item refnum - primary key (assigned automatically for new referrals) + +=item referral - Text name of this referral + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new referral. To add the referral to the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('part_referral')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('part_referral',$hashref); +} + +=item insert + +Adds this referral to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. + +=cut + +sub delete { + my($self)=@_; + return "Can't (yet?) delete part_referral records"; + #$self->del; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not an part_referral record!" + unless $old->table eq "part_referral"; + return "Can't change refnum!" + unless $old->getfield('refnum') eq $new->getfield('refnum'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid referral. If there is an error, +returns the error, otherwise returns false. Called by the insert and replace +methods. + +=cut + +sub check { + my($self)=@_; + return "Not a part_referral record!" unless $self->table eq "part_referral"; + + my($error)= + $self->ut_numbern('refnum') + or $self->ut_text('referral') + ; + return $error if $error; + + ''; + +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +The delete method is unimplemented. + +=head1 SEE ALSO + +L, L, schema.html from the base documentation. + +=head1 HISTORY + +Class dealing with referrals + +ivan@sisd.com 98-feb-23 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + diff --git a/site_perl/pkg_svc.pm b/site_perl/pkg_svc.pm new file mode 100644 index 000000000..517125c01 --- /dev/null +++ b/site_perl/pkg_svc.pm @@ -0,0 +1,168 @@ +package FS::pkg_svc; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields hfields qsearchs); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(hfields); + +=head1 NAME + +FS::pkg_svc - Object methods for pkg_svc records + +=head1 SYNOPSIS + + use FS::pkg_svc; + + $record = create FS::pkg_svc \%hash; + $record = create FS::pkg_svc { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::pkg_svc record links a billing item definition (see L) to +a service definition (see L). FS::pkg_svc inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item pkgpart - Billing item definition (see L) + +=item svcpart - Service definition (see L) + +=item quantity - Quantity of this service definition that this billing item +definition includes + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Create a new record. To add the record to the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('pkg_svc')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('pkg_svc',$hashref); + +} + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Deletes this record from the database. If there is an error, returns the +error, otherwise returns false. + +=cut + +sub delete { + my($self)=@_; + + $self->del; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a pkg_svc record!" unless $old->table eq "pkg_svc"; + return "Can't change pkgpart!" + if $old->getfield('pkgpart') ne $new->getfield('pkgpart'); + return "Can't change svcpart!" + if $old->getfield('svcpart') ne $new->getfield('svcpart'); + + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid record. If there is an error, +returns the error, otherwise returns false. Called by the insert and replace +methods. + +=cut + +sub check { + my($self)=@_; + return "Not a pkg_svc record!" unless $self->table eq "pkg_svc"; + my($recref) = $self->hashref; + + my($error); + return $error if $error = + $self->ut_number('pkgpart') + || $self->ut_number('svcpart') + || $self->ut_number('quantity') + ; + + return "Unknown pkgpart!" + unless qsearchs('part_pkg',{'pkgpart'=> $self->getfield('pkgpart')}); + + return "Unknown svcpart!" + unless qsearchs('part_svc',{'svcpart'=> $self->getfield('svcpart')}); + + ''; #no error +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=head1 HISTORY + +ivan@voicenet.com 97-jul-1 + +added hfields +ivan@sisd.com 97-nov-13 + +pod ivan@sisd.com 98-sep-22 + +=cut + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 8de52f38c97e11bb5b5995e57f446461fdc8ba6c Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 24 Sep 1998 05:15:09 +0000 Subject: Initial revision --- site_perl/part_svc.pm | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 site_perl/part_svc.pm diff --git a/site_perl/part_svc.pm b/site_perl/part_svc.pm new file mode 100644 index 000000000..0fd8ee47d --- /dev/null +++ b/site_perl/part_svc.pm @@ -0,0 +1,199 @@ +package FS::part_svc; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields hfields); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(hfields fields); + +=head1 NAME + +FS::part_svc - Object methods for part_svc objects + +=head1 SYNOPSIS + + use FS::part_svc; + + $record = create FS::part_referral \%hash + $record = create FS::part_referral { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::part_svc represents a service definition. FS::part_svc inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item svcpart - primary key (assigned automatically for new service definitions) + +=item svc - text name of this service definition + +=item svcdb - table used for this service. See L, +L, and L, among others. + +=item I__I - Default or fixed value for I in I. + +=item I__I_flag - defines I__I action: null, `D' for default, or `F' for fixed + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new service definition. To add the service definition to the +database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('part_svc')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('part_svc',$hashref); +} + +=item insert + +Adds this service definition to the database. If there is an error, returns +the error, otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. + +=cut + +sub delete { + return "Can't (yet?) delete service definitions."; +# maybe check & make sure the svcpart isn't in cust_svc or (in any packages)? +# my($self)=@_; +# +# $self->del; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not a part_svc record!" unless $old->table eq "part_svc"; + return "Can't change svcpart!" + unless $old->getfield('svcpart') eq $new->getfield('svcpart'); + return "Can't change svcdb!" + unless $old->getfield('svcdb') eq $new->getfield('svcdb'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid service definition. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +sub check { + my($self)=@_; + return "Not a part_svc record!" unless $self->table eq "part_svc"; + my($recref) = $self->hashref; + + my($error); + return $error if $error= + $self->ut_numbern('svcpart') + || $self->ut_text('svc') + || $self->ut_alpha('svcdb') + ; + + my(@fields) = eval { fields($recref->{svcdb}) }; #might die + return "Unknown svcdb!" unless @fields; + + my($svcdb); + foreach $svcdb ( qw( + svc_acct svc_acct_sm svc_charge svc_domain svc_wo + ) ) { + my(@rows)=map { /^${svcdb}__(.*)$/; $1 } + grep ! /_flag$/, + grep /^${svcdb}__/, + fields('part_svc'); + my($row); + foreach $row (@rows) { + unless ( $svcdb eq $recref->{svcdb} ) { + $recref->{$svcdb.'__'.$row}=''; + $recref->{$svcdb.'__'.$row.'_flag'}=''; + next; + } + $recref->{$svcdb.'__'.$row.'_flag'} =~ /^([DF]?)$/ + or return "Illegal flag for $svcdb $row"; + $recref->{$svcdb.'__'.$row.'_flag'} = $1; + +# $recref->{$svcdb.'__'.$row} =~ /^(.*)$/ #not restrictive enough? +# or return "Illegal value for $svcdb $row"; +# $recref->{$svcdb.'__'.$row} = $1; + my($error); + return $error if $error=$self->ut_anything($svcdb.'__'.$row); + + } + } + + ''; #no error +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +Delete is unimplemented. + +=head1 SEE ALSO + +L, L, L, L, +L, L, L, schema.html from the +base documentation. + +=head1 HISTORY + +ivan@sisd.com 97-nov-14 + +data checking/untainting calls into FS::Record added +ivan@sisd.com 97-dec-6 + +pod ivan@sisd.com 98-sep-21 + +=cut + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 4fe44937087247a57a952b292b50b1e63531b8ed Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 24 Sep 1998 06:20:57 +0000 Subject: Initial revision --- site_perl/svc_acct_pop.pm | 163 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 site_perl/svc_acct_pop.pm diff --git a/site_perl/svc_acct_pop.pm b/site_perl/svc_acct_pop.pm new file mode 100644 index 000000000..a6f801f22 --- /dev/null +++ b/site_perl/svc_acct_pop.pm @@ -0,0 +1,163 @@ +package FS::svc_acct_pop; + +use strict; +use vars qw(@ISA @EXPORT_OK); +use Exporter; +use FS::Record qw(fields qsearchs); + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +=head1 NAME + +FS::svc_acct_pop - Object methods for svc_acct_pop records + +=head1 SYNOPSIS + + use FS::svc_acct_pop; + + $record = create FS::svc_acct_pop \%hash; + $record = create FS::svc_acct_pop { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::svc_acct object represents an point of presence. FS::svc_acct_pop +inherits from FS::Record. The following fields are currently supported: + +=over 4 + +=item popnum - primary key (assigned automatically for new accounts) + +=item city + +=item state + +=item ac - area code + +=item exch - exchange + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new point of presence (if only it were that easy!). To add the +point of presence to the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('svc_acct_pop')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('svc_acct_pop',$hashref); +} + +=item insert + +Adds this point of presence to the databaes. If there is an error, returns the +error, otherwise returns false. + +=cut + +sub insert { + my($self)=@_; + + $self->check or + $self->add; +} + +=item delete + +Currently unimplemented. + +=cut + +sub delete { + my($self)=@_; + return "Can't (yet) delete POPs!"; + #$self->del; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + return "(Old) Not an svc_acct_pop record!" + unless $old->table eq "svc_acct_pop"; + return "Can't change popnum!" + unless $old->getfield('popnum') eq $new->getfield('popnum'); + $new->check or + $new->rep($old); +} + +=item check + +Checks all fields to make sure this is a valid point of presence. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +sub check { + my($self)=@_; + return "Not a svc_acct_pop record!" unless $self->table eq "svc_acct_pop"; + + my($error)= + $self->ut_numbern('popnum') + or $self->ut_text('city') + or $self->ut_text('state') + or $self->ut_number('ac') + or $self->ut_number('exch') + ; + return $error if $error; + + ''; + +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +It should be renamed to part_pop. + +=head1 SEE ALSO + +L, L, schema.html from the base documentation. + +=head1 HISTORY + +Class dealing with pops + +ivan@sisd.com 98-mar-8 + +pod ivan@sisd.com 98-sep-23 + +=cut + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 4c686015cf196f09d5870712f7a64afaa1d1c9be Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 24 Sep 1998 08:18:53 +0000 Subject: Initial revision --- site_perl/svc_acct_sm.pm | 350 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 site_perl/svc_acct_sm.pm diff --git a/site_perl/svc_acct_sm.pm b/site_perl/svc_acct_sm.pm new file mode 100644 index 000000000..c87ed2c54 --- /dev/null +++ b/site_perl/svc_acct_sm.pm @@ -0,0 +1,350 @@ +package FS::svc_acct_sm; + +use strict; +use vars qw(@ISA @EXPORT_OK $nossh_hack $conf $shellmachine @qmailmachines); +use Exporter; +use FS::Record qw(fields qsearch qsearchs); +use FS::cust_svc; +use FS::SSH qw(ssh); +use FS::Conf; + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +$conf = new FS::Conf; + +$shellmachine = $conf->exists('qmailmachines') + ? $conf->config('shellmachine') + : ''; + +=head1 NAME + +FS::svc_acct_sm - Object methods for svc_acct_sm records + +=head1 SYNOPSIS + + use FS::svc_acct_sm; + + $record = create FS::svc_acct_sm \%hash; + $record = create FS::svc_acct_sm { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + + $error = $record->suspend; + + $error = $record->unsuspend; + + $error = $record->cancel; + +=head1 DESCRIPTION + +An FS::svc_acct object represents a virtual mail alias. FS::svc_acct inherits +from FS::Record. The following fields are currently supported: + +=over 4 + +=item svcnum - primary key (assigned automatcially for new accounts) + +=item domsvc - svcnum of the virtual domain (see L) + +=item domuid - uid of the target account (see L) + +=item domuser - virtual username + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new virtual mail alias. To add the virtual mail alias to the +database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('svc_acct_sm')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('svc_acct_sm',$hashref); + +} + +=item insert + +Adds this virtual mail alias to the database. If there is an error, returns +the error, otherwise returns false. + +The additional fields pkgnum and svcpart (see L) should be +defined. An FS::cust_svc record will be created and inserted. + +If the configuration values (see L) shellmachine and qmailmachines +exist, and domuser is `*' (meaning a catch-all mailbox), the command: + + [ -e $dir/.qmail-$qdomain-default ] || { + touch $dir/.qmail-$qdomain-default; + chown $uid:$gid $dir/.qmail-$qdomain-default; + } + +is executed on shellmachine via ssh (see L). +This behaviour can be surpressed by setting $FS::svc_acct_sm::nossh_hack true. + +=cut + +sub insert { + my($self)=@_; + my($error); + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + $error=$self->check; + return $error if $error; + + return "Domain username (domuser) in use for this domain (domsvc)" + if qsearchs('svc_acct_sm',{ 'domuser'=> $self->domuser, + 'domsvc' => $self->domsvc, + } ); + + return "First domain username (domuser) for domain (domsvc) must be " . + qq='*' (catch-all)!= + if $self->domuser ne '*' && + ! qsearch('svc_acct_sm',{ 'domsvc' => $self->domsvc } ); + + my($svcnum)=$self->getfield('svcnum'); + my($cust_svc); + unless ( $svcnum ) { + $cust_svc=create FS::cust_svc ( { + 'svcnum' => $svcnum, + 'pkgnum' => $self->getfield('pkgnum'), + 'svcpart' => $self->getfield('svcpart'), + } ); + my($error) = $cust_svc->insert; + return $error if $error; + $svcnum = $self->setfield('svcnum',$cust_svc->getfield('svcnum')); + } + + $error = $self->add; + if ($error) { + $cust_svc->del if $cust_svc; + return $error; + } + + my $svc_domain = qsearchs('svc_domain',{'svcnum'=> $self->domsvc } ); + my $svc_acct = qsearchs('svc_acct',{'uid'=> $self->domuid } ); + my($uid,$gid,$dir,$domain)=( + $svc_acct->getfield('uid'), + $svc_acct->getfield('gid'), + $svc_acct->getfield('dir'), + $svc_domain->getfield('domain') + ); + my($qdomain)=$domain; + $qdomain =~ s/\./:/g; #see manpage for 'dot-qmail': EXTENSION ADDRESSES + ssh("root\@$shellmachine","[ -e $dir/.qmail-$qdomain-default ] || { touch $dir/.qmail-$qdomain-default; chown $uid:$gid $dir/.qmail-$qdomain-default; }") + if ( ! $nossh_hack && $shellmachine && $dir && $self->domuser eq '*' ); + + ''; #no error + +} + +=item delete + +Deletes this virtual mail alias from the database. If there is an error, +returns the error, otherwise returns false. + +The corresponding FS::cust_svc record will be deleted as well. + +=cut + +sub delete { + my($self)=@_; + my($error); + + my($svcnum)=$self->getfield('svcnum'); + + $error = $self->del; + return $error if $error; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}); + $error = $cust_svc->del; + return $error if $error; + + ''; + +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my($new,$old)=@_; + my($error); + + return "(Old) Not a svc_acct_sm record!" unless $old->table eq "svc_acct_sm"; + return "Can't change svcnum!" + unless $old->getfield('svcnum') eq $new->getfield('svcnum'); + + return "Domain username (domuser) in use for this domain (domsvc)" + if ( $old->domuser ne $new->domuser + || $old->domsvc ne $new->domsvc + ) && qsearchs('svc_acct_sm',{ + 'domuser'=> $new->domuser, + 'domsvc' => $new->domsvc, + } ) + ; + + $error=$new->check; + return $error if $error; + + $error = $new->rep($old); + return $error if $error; + + ''; #no error +} + +=item suspend + +Just returns false (no error) for now. + +Called by the suspend method of FS::cust_pkg (see L). + +=cut + +sub suspend { + ''; #no error (stub) +} + +=item unsuspend + +Just returns false (no error) for now. + +Called by the unsuspend method of FS::cust_pkg (see L). + +=cut + +sub unsuspend { + ''; #no error (stub) +} + +=item cancel + +Just returns false (no error) for now. + +Called by the cancel method of FS::cust_pkg (see L). + +=cut + +sub cancel { + ''; #no error (stub) +} + +=item check + +Checks all fields to make sure this is a valid virtual mail alias. If there is +an error, returns the error, otherwise returns false. Called by the insert and +replace methods. + +Sets any fixed values; see L. + +=cut + +sub check { + my($self)=@_; + return "Not a svc_acct_sm record!" unless $self->table eq "svc_acct_sm"; + my($recref) = $self->hashref; + + $recref->{svcnum} =~ /^(\d*)$/ or return "Illegal svcnum"; + $recref->{svcnum} = $1; + + #get part_svc + my($svcpart); + my($svcnum)=$self->getfield('svcnum'); + if ($svcnum) { + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}); + return "Unknown svcnum" unless $cust_svc; + $svcpart=$cust_svc->svcpart; + } else { + $svcpart=$self->getfield('svcpart'); + } + my($part_svc)=qsearchs('part_svc',{'svcpart'=>$svcpart}); + return "Unkonwn svcpart" unless $part_svc; + + #set fixed fields from part_svc + my($field); + foreach $field ( fields('svc_acct_sm') ) { + if ( $part_svc->getfield('svc_acct_sm__'. $field. '_flag') eq 'F' ) { + $self->setfield($field,$part_svc->getfield('svc_acct_sm__'. $field) ); + } + } + + $recref->{domuser} =~ /^(\*|[a-z0-9_\-]{2,32})$/ + or return "Illegal domain username (domuser)"; + $recref->{domuser} = $1; + + $recref->{domsvc} =~ /^(\d+)$/ or return "Illegal domsvc"; + $recref->{domsvc} = $1; + my($svc_domain); + return "Unknown domsvc" unless + $svc_domain=qsearchs('svc_domain',{'svcnum'=> $recref->{domsvc} } ); + + $recref->{domuid} =~ /^(\d+)$/ or return "Illegal uid"; + $recref->{domuid} = $1; + my($svc_acct); + return "Unknown uid" unless + $svc_acct=qsearchs('svc_acct',{'uid'=> $recref->{domuid} } ); + + ''; #no error +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +The remote commands should be configurable. + +=head1 SEE ALSO + +L, L, L, L, L, +L, L, L, L, L, +schema.html from the base documentation. + +=head1 HISTORY + +ivan@voicenet.com 97-jul-16 - 21 + +rewrite ivan@sisd.com 98-mar-10 + +s/qsearchs/qsearch/ to eliminate warning ivan@sisd.com 98-apr-19 + +uses conf/shellmachine and has an nossh_hack ivan@sisd.com 98-jul-14 + +s/\./:/g in .qmail-domain:com ivan@sisd.com 98-aug-13 + +pod, FS::Conf, moved .qmail file from check to insert 98-sep-23 + +=cut + +1; + -- cgit v1.2.1 -- cgit v1.2.1 From 1e97e6bad3ee2bd47a56253d2c30fcfab87c43d4 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 24 Sep 1998 08:59:01 +0000 Subject: Initial revision --- site_perl/svc_acct.pm | 557 ++++++++++++++++++++++++++++++++++++++++++++++++ site_perl/svc_domain.pm | 539 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1096 insertions(+) create mode 100644 site_perl/svc_acct.pm create mode 100644 site_perl/svc_domain.pm diff --git a/site_perl/svc_acct.pm b/site_perl/svc_acct.pm new file mode 100644 index 000000000..a43af6b1a --- /dev/null +++ b/site_perl/svc_acct.pm @@ -0,0 +1,557 @@ +package FS::svc_acct; + +use strict; +use vars qw(@ISA @EXPORT_OK $nossh_hack $conf $dir_prefix @shells + $shellmachine @saltset @pw_set); +use Exporter; +use FS::Conf; +use FS::Record qw(fields qsearchs); +use FS::SSH qw(ssh); +use FS::cust_svc; + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +$conf = new FS::Conf; +$dir_prefix = $conf->config('home'); +@shells = $conf->config('shells'); +$shellmachine = $conf->config('shellmachine'); + +@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' ); +@pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '!', '.', ',' ); + +#not needed in 5.004 #srand($$|time); + +=head1 NAME + +FS::svc_acct - Object methods for svc_acct records + +=head1 SYNOPSIS + + use FS::svc_acct; + + $record = create FS::svc_acct \%hash; + $record = create FS::svc_acct { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + + $error = $record->suspend; + + $error = $record->unsuspend; + + $error = $record->cancel; + +=head1 DESCRIPTION + +An FS::svc_acct object represents an account. FS::svc_acct inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item svcnum - primary key (assigned automatcially for new accounts) + +=item username + +=item _password - generated if blank + +=item popnum - Point of presence (see L) + +=item uid + +=item gid + +=item finger - GECOS + +=item dir - set automatically if blank (and uid is not) + +=item shell + +=item quota - (unimplementd) + +=item slipip - IP address + +=item radius_I - I + +=back + +=head1 METHODS + +=over 4 + +=item create HASHREF + +Creates a new account. To add the account to the database, see L<"insert">. + +=cut + +sub create { + my($proto,$hashref)=@_; + + #now in FS::Record::new + #my($field); + #foreach $field (fields('svc_acct')) { + # $hashref->{$field}='' unless defined $hashref->{$field}; + #} + + $proto->new('svc_acct',$hashref); + +} + +=item insert + +Adds this account to the database. If there is an error, returns the error, +otherwise returns false. + +The additional fields pkgnum and svcpart (see L) should be +defined. An FS::cust_svc record will be created and inserted. + +If the configuration value (see L) shellmachine exists, and the +username, uid, and dir fields are defined, the command + + useradd -d $dir -m -s $shell -u $uid $username + +is executed on shellmachine via ssh. This behaviour can be surpressed by +setting $FS::svc_acct::nossh_hack true. + +=cut + +sub insert { + my($self)=@_; + my($error); + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + $error=$self->check; + return $error if $error; + + return "Username ". $self->username. " in use" + if qsearchs('svc_acct',{'username'=> $self->username } ); + + my($part_svc) = qsearchs('part_svc',{ 'svcpart' => $self->svcpart }); + return "Unkonwn svcpart" unless $part_svc; + return "uid in use" + if $part_svc->svc_acct__uid_flag ne 'F' + && qsearchs('svc_acct',{'uid'=> $self->uid } ) + && $self->username !~ /^(hyla)?fax$/ + ; + + my($svcnum)=$self->svcnum; + my($cust_svc); + unless ( $svcnum ) { + $cust_svc=create FS::cust_svc ( { + 'svcnum' => $svcnum, + 'pkgnum' => $self->pkgnum, + 'svcpart' => $self->svcpart, + } ); + my($error) = $cust_svc->insert; + return $error if $error; + $svcnum = $self->svcnum($cust_svc->svcnum); + } + + $error = $self->add; + if ($error) { + #$cust_svc->del if $cust_svc; + $cust_svc->delete if $cust_svc; + return $error; + } + + my($username,$uid,$dir,$shell) = ( + $self->username, + $self->uid, + $self->dir, + $self->shell, + ); + if ( $username + && $uid + && $dir + && $shellmachine + && ! $nossh_hack ) { + #one way + ssh("root\@$shellmachine", + "useradd -d $dir -m -s $shell -u $uid $username" + ); + #another way + #ssh("root\@$shellmachine","/bin/mkdir $dir; /bin/chmod 711 $dir; ". + # "/bin/cp -p /etc/skel/.* $dir 2>/dev/null; ". + # "/bin/cp -pR /etc/skel/Maildir $dir 2>/dev/null; ". + # "/bin/chown -R $uid $dir") unless $nossh_hack; + } + + ''; #no error +} + +=item delete + +Deletes this account from the database. If there is an error, returns the +error, otherwise returns false. + +The corresponding FS::cust_svc record will be deleted as well. + +If the configuration value (see L) shellmachine exists, the command: + + userdel $username + +is executed on shellmachine via ssh. This behaviour can be surpressed by +setting $FS::svc_acct::nossh_hack true. + +=cut + +sub delete { + my($self)=@_; + my($error); + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + my($svcnum)=$self->getfield('svcnum'); + + $error = $self->del; + return $error if $error; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}); + $error = $cust_svc->del; + return $error if $error; + + my($username) = $self->getfield('username'); + if ( $username && $shellmachine && ! $nossh_hack ) { + ssh("root\@$shellmachine","userdel $username"); + } + + ''; +} + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +If the configuration value (see L) shellmachine exists, and the +dir field has changed, the command: + + [ -d $old_dir ] && ( + chmod u+t $old_dir; + umask 022; + mkdir $new_dir; + cd $old_dir; + find . -depth -print | cpio -pdm $new_dir; + chmod u-t $new_dir; + chown -R $uid.$gid $new_dir; + rm -rf $old_dir + ) + +is executed on shellmachine via ssh. This behaviour can be surpressed by +setting $FS::svc_acct::nossh_hack true. + +=cut + +sub replace { + my($new,$old)=@_; + my($error); + + return "(Old) Not a svc_acct record!" unless $old->table eq "svc_acct"; + return "Can't change svcnum!" + unless $old->getfield('svcnum') eq $new->getfield('svcnum'); + + return "Username in use" + if $old->getfield('username') ne $new->getfield('username') && + qsearchs('svc_acct',{'username'=> $new->getfield('username') } ); + + return "Can't change uid!" + if $old->getfield('uid') ne $new->getfield('uid'); + + #change homdir when we change username + if ( $old->getfield('username') ne $new->getfield('username') ) { + $new->setfield('dir',''); + } + + $error=$new->check; + return $error if $error; + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + + $error = $new->rep($old); + return $error if $error; + + my($old_dir,$new_dir)=( $old->getfield('dir'),$new->getfield('dir') ); + my($uid,$gid)=( $new->getfield('uid'), $new->getfield('gid') ); + if ( $old_dir + && $new_dir + && $old_dir ne $new_dir + && ! $nossh_hack + ) { + ssh("root\@$shellmachine","[ -d $old_dir ] && ". + "( chmod u+t $old_dir; ". #turn off qmail delivery + "umask 022; mkdir $new_dir; cd $old_dir; ". + "find . -depth -print | cpio -pdm $new_dir; ". + "chmod u-t $new_dir; chown -R $uid.$gid $new_dir; ". + "rm -rf $old_dir". + ")" + ); + } + + ''; #no error +} + +=item suspend + +Suspends this account by prefixing *SUSPENDED* to the password. If there is an +error, returns the error, otherwise returns false. + +Called by the suspend method of FS::cust_pkg (see L). + +=cut + +sub suspend { + my($old) = @_; + my(%hash) = $old->hash; + unless ( $hash{_password} =~ /^\*SUSPENDED\* / ) { + $hash{_password} = '*SUSPENDED* '.$hash{_password}; + my($new) = create FS::svc_acct ( \%hash ); +# $new->replace($old); + $new->rep($old); #to avoid password checking :) + } else { + ''; #no error (already suspended) + } + +} + +=item unsuspend + +Unsuspends this account by removing *SUSPENDED* from the password. If there is +an error, returns the error, otherwise returns false. + +Called by the unsuspend method of FS::cust_pkg (see L). + +=cut + +sub unsuspend { + my($old) = @_; + my(%hash) = $old->hash; + if ( $hash{_password} =~ /^\*SUSPENDED\* (.*)$/ ) { + $hash{_password} = $1; + my($new) = create FS::svc_acct ( \%hash ); +# $new->replace($old); + $new->rep($old); #to avoid password checking :) + } else { + ''; #no error (already unsuspended) + } +} + +=item cancel + +Just returns false (no error) for now. + +Called by the cancel method of FS::cust_pkg (see L). + +=cut + +# Usage: $error = $record -> cancel; +sub cancel { + ''; #stub (no error) - taken care of in delete +} + +=item check + +Checks all fields to make sure this is a valid service. If there is an error, +returns the error, otherwise returns false. Called by the insert and replace +methods. + +Sets any fixed values; see L. + +=cut + +sub check { + my($self)=@_; + return "Not a svc_acct record!" unless $self->table eq "svc_acct"; + my($recref) = $self->hashref; + + $recref->{svcnum} =~ /^(\d*)$/ or return "Illegal svcnum"; + $recref->{svcnum} = $1; + + #get part_svc + my($svcpart); + my($svcnum)=$self->getfield('svcnum'); + if ($svcnum) { + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}); + return "Unknown svcnum" unless $cust_svc; + $svcpart=$cust_svc->svcpart; + } else { + $svcpart=$self->getfield('svcpart'); + } + my($part_svc)=qsearchs('part_svc',{'svcpart'=>$svcpart}); + return "Unkonwn svcpart" unless $part_svc; + + #set fixed fields from part_svc + my($field); + foreach $field ( fields('svc_acct') ) { + if ( $part_svc->getfield('svc_acct__'. $field. '_flag') eq 'F' ) { + $self->setfield($field,$part_svc->getfield('svc_acct__'. $field) ); + } + } + + my($ulen)=$self->dbdef_table->column('username')->length; + $recref->{username} =~ /^([a-z0-9_\-]{2,$ulen})$/ + or return "Illegal username"; + $recref->{username} = $1; + $recref->{username} =~ /[a-z]/ or return "Illegal username"; + + $recref->{popnum} =~ /^(\d*)$/ or return "Illegal popnum"; + $recref->{popnum} = $1; + return "Unkonwn popnum" unless + ! $recref->{popnum} || + qsearchs('svc_acct_pop',{'popnum'=> $recref->{popnum} } ); + + unless ( $part_svc->getfield('svc_acct__uid_flag') eq 'F' ) { + + $recref->{uid} =~ /^(\d*)$/ or return "Illegal uid"; + $recref->{uid} = $1 eq '' ? $self->unique('uid') : $1; + + $recref->{gid} =~ /^(\d*)$/ or return "Illegal gid"; + $recref->{gid} = $1 eq '' ? $recref->{uid} : $1; + #not all systems use gid=uid + #you can set a fixed gid in part_svc + + return "Only root can have uid 0" + if $recref->{uid} == 0 && $recref->{username} ne 'root'; + + my($error); + return $error if $error=$self->ut_textn('finger'); + + $recref->{dir} =~ /^([\/\w\-]*)$/ + or return "Illegal directory"; + $recref->{dir} = $1 || + $dir_prefix . '/' . $recref->{username} + #$dir_prefix . '/' . substr($recref->{username},0,1). '/' . $recref->{username} + ; + + unless ( $recref->{username} eq 'sync' ) { + my($shell); + if ( $shell = (grep $_ eq $recref->{shell}, @shells)[0] ) { + $recref->{shell} = $shell; + } else { + return "Illegal shell ". $self->shell; + } + } else { + $recref->{shell} = '/bin/sync'; + } + + $recref->{quota} =~ /^(\d*)$/ or return "Illegal quota (unimplemented)"; + $recref->{quota} = $1; + + } else { + $recref->{gid} ne '' ? + return "Can't have gid without uid" : ( $recref->{gid}='' ); + $recref->{finger} ne '' ? + return "Can't have finger-name without uid" : ( $recref->{finger}='' ); + $recref->{dir} ne '' ? + return "Can't have directory without uid" : ( $recref->{dir}='' ); + $recref->{shell} ne '' ? + return "Can't have shell without uid" : ( $recref->{shell}='' ); + $recref->{quota} ne '' ? + return "Can't have quota without uid" : ( $recref->{quota}='' ); + } + + unless ( $part_svc->getfield('svc_acct__slipip_flag') eq 'F' ) { + unless ( $recref->{slipip} eq '0e0' ) { + $recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ + or return "Illegal slipip". $self->slipip; + $recref->{slipip} = $1; + } else { + $recref->{slipip} = '0e0'; + } + + } + + #arbitrary RADIUS stuff; allow ut_textn for now + foreach ( grep /^radius_/, fields('svc_acct') ) { + $self->ut_textn($_); + } + + #generate a password if it is blank + $recref->{_password} = join('',map($pw_set[ int(rand $#pw_set) ], (0..7) ) ) + unless ( $recref->{_password} ); + + #if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{4,16})$/ ) { + if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{4,8})$/ ) { + $recref->{_password} = $1.$3; + #uncomment this to encrypt password immediately upon entry, or run + #bin/crypt_pw in cron to give new users a window during which their + #password is available to techs, for faxing, etc. (also be aware of + #radius issues!) + #$recref->{password} = $1. + # crypt($3,$saltset[int(rand(64))].$saltset[int(rand(64))] + #; + } elsif ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([\w\.\/]{13,24})$/ ) { + $recref->{_password} = $1.$3; + } elsif ( $recref->{_password} eq '*' ) { + $recref->{_password} = '*'; + } else { + return "Illegal password"; + } + + ''; #no error +} + +=back + +=head1 BUGS + +It doesn't properly override FS::Record yet. + +The remote commands should be configurable. + +The create method should set defaults from part_svc (like the check method +sets fixed values). + +=head1 SEE ALSO + +L, L, L, L, L, +L, L, L, schema.html from the base +documentation. + +=head1 HISTORY + +ivan@voicenet.com 97-jul-16 - 21 + +rewrite (among other things, now know about part_svc) ivan@sisd.com 98-mar-8 + +Changed 'password' to '_password' because Pg6.3 reserves the password word + bmccane@maxbaud.net 98-apr-3 + +username length and shell no longer hardcoded ivan@sisd.com 98-jun-28 + +eww but needed: ignore uid duplicates for 'fax' and 'hylafax' +ivan@sisd.com 98-jun-29 + +$nossh_hack ivan@sisd.com 98-jul-13 + +protections against UID/GID of 0 for incorrectly-setup RDBMSs (also +in bin/svc_acct.export) ivan@sisd.com 98-jul-13 + +arbitrary radius attributes ivan@sisd.com 98-aug-13 + +/var/spool/freeside/conf/shellmachine ivan@sisd.com 98-aug-13 + +pod and FS::conf ivan@sisd.com 98-sep-22 + +=cut + +1; + diff --git a/site_perl/svc_domain.pm b/site_perl/svc_domain.pm new file mode 100644 index 000000000..1ddd5b290 --- /dev/null +++ b/site_perl/svc_domain.pm @@ -0,0 +1,539 @@ +package FS::svc_domain; + +use strict; +use vars qw(@ISA @EXPORT_OK $whois_hack $conf $mydomain $smtpmachine); +use Exporter; +use Carp; +use Mail::Internet; +use Mail::Header; +use Date::Format; +use FS::Record qw(fields qsearch qsearchs); +use FS::cust_svc; +use FS::Conf; + +@ISA = qw(FS::Record Exporter); +@EXPORT_OK = qw(fields); + +$conf = new FS::Conf; + +$mydomain = $conf->config('domain'); +$smtpmachine = $conf->config('smtpmachine'); + +my($internic)="/var/spool/freeside/conf/registries/internic"; +my($conf_tech)="$internic/tech_contact"; +my($conf_from)="$internic/from"; +my($conf_to)="$internic/to"; +my($nameservers)="$internic/nameservers"; +my($template)="$internic/template"; + +open(TECH_CONTACT,$conf_tech) or die "Can't open $conf_tech: $!"; +my($tech_contact)=map { + /^(.*)$/ or die "Illegal line in $conf_tech!"; #yes, we trust the file + $1; +} grep $_ !~ /^(#|$)/, ; +close TECH_CONTACT; + +open(FROM,$conf_from) or die "Can't open $conf_from: $!"; +my($from)=map { + /^(.*)$/ or die "Illegal line in $conf_from!"; #yes, we trust the file + $1; +} grep $_ !~ /^(#|$)/, ; +close FROM; + +open(TO,$conf_to) or die "Can't open $conf_to: $!"; +my($to)=map { + /^(.*)$/ or die "Illegal line in $conf_to!"; #yes, we trust the file + $1; +} grep $_ !~ /^(#|$)/, ; +close TO; + +open(NAMESERVERS,$nameservers) or die "Can't open $nameservers: $!"; +my(@nameservers)=map { + /^\s*\d+\.\d+\.\d+\.\d+\s+([^\s]+)\s*$/ + or die "Illegal line in $nameservers!"; #yes, we trust the file + $1; +} grep $_ !~ /^(#|$)/, ; +close NAMESERVERS; +open(NAMESERVERS,$nameservers) or die "Can't open $nameservers: $!"; +my(@nameserver_ips)=map { + /^\s*(\d+\.\d+\.\d+\.\d+)\s+([^\s]+)\s*$/ + or die "Illegal line in $nameservers!"; #yes, we trust the file + $1; +} grep $_ !~ /^(#|$)/, ; +close NAMESERVERS; + +open(TEMPLATE,$template) or die "Can't open $template: $!"; +my(@template)=map { + /^(.*)$/ or die "Illegal line in $to!"; #yes, we trust the file + $1. "\n"; +}