1 #============================================================= -*-perl-*-
3 # BackupPC::CGI::EditConfig package
7 # This module implements the EditConfig action for the CGI interface.
10 # Craig Barratt <cbarratt@users.sourceforge.net>
13 # Copyright (C) 2005-2007 Craig Barratt
15 # This program is free software; you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation; either version 2 of the License, or
18 # (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #========================================================================
31 # Version 3.1.1, released 22 Dec 2008.
33 # See http://backuppc.sourceforge.net.
35 #========================================================================
37 package BackupPC::CGI::EditConfig;
40 use BackupPC::CGI::Lib qw(:all);
41 use BackupPC::Config::Meta qw(:all);
42 use BackupPC::Storage;
48 text => "CfgEdit_Title_Server",
50 {text => "CfgEdit_Title_General_Parameters"},
51 {name => "ServerHost"},
52 {name => "BackupPCUser"},
53 {name => "BackupPCUserVerify"},
54 {name => "MaxOldLogFiles"},
55 {name => "TrashCleanSleepSec"},
57 {text => "CfgEdit_Title_Wakeup_Schedule"},
58 {name => "WakeupSchedule"},
60 {text => "CfgEdit_Title_Concurrent_Jobs"},
61 {name => "MaxBackups"},
62 {name => "MaxUserBackups"},
63 {name => "MaxPendingCmds"},
64 {name => "MaxBackupPCNightlyJobs"},
65 {name => "BackupPCNightlyPeriod"},
67 {text => "CfgEdit_Title_Pool_Filesystem_Limits"},
69 {name => "DfMaxUsagePct"},
70 {name => "HardLinkMax"},
72 {text => "CfgEdit_Title_Other_Parameters"},
73 {name => "UmaskMode"},
75 {name => "DHCPAddressRanges"},
76 {name => "PerlModuleLoad"},
77 {name => "ServerInitdPath"},
78 {name => "ServerInitdStartCmd"},
80 {text => "CfgEdit_Title_Remote_Apache_Settings"},
81 {name => "ServerPort"},
82 {name => "ServerMesgSecret"},
84 {text => "CfgEdit_Title_Program_Paths"},
86 {name => "NmbLookupPath"},
89 {name => "SplitPath"},
93 {name => "Bzip2Path"},
95 {text => "CfgEdit_Title_Install_Paths"},
97 # Can only edit TopDir and LogDir if we are in FHS mode.
98 # Otherwise they are hardcoded in lib/BackupPC/Lib.pm.
101 visible => sub { return $_[1]->useFHS(); } },
103 visible => sub { return $_[1]->useFHS(); } },
106 # Cannot edit ConfDir or InstallDir, since the real value is hardcoded in
107 # lib/BackupPC/Lib.pm.
108 # {name => "ConfDir"},
109 # {name => "InstallDir"},
114 text => "CfgEdit_Title_Email",
116 {text => "CfgEdit_Title_Email_settings"},
117 {name => "SendmailPath"},
118 {name => "EMailNotifyMinDays"},
119 {name => "EMailFromUserName"},
120 {name => "EMailAdminUserName"},
121 {name => "EMailUserDestDomain"},
123 {text => "CfgEdit_Title_Email_User_Messages"},
124 {name => "EMailNoBackupEverSubj"},
125 {name => "EMailNoBackupEverMesg"},
126 {name => "EMailNotifyOldBackupDays"},
127 {name => "EMailNoBackupRecentSubj"},
128 {name => "EMailNoBackupRecentMesg"},
129 {name => "EMailNotifyOldOutlookDays"},
130 {name => "EMailOutlookBackupSubj"},
131 {name => "EMailOutlookBackupMesg"},
132 {name => "EMailHeaders"},
136 text => "CfgEdit_Title_CGI",
138 {text => "CfgEdit_Title_Admin_Privileges"},
139 {name => "CgiAdminUserGroup"},
140 {name => "CgiAdminUsers"},
142 {text => "CfgEdit_Title_Page_Rendering"},
143 {name => "Language"},
144 {name => "CgiNavBarAdminAllHosts"},
145 {name => "CgiSearchBoxEnable"},
146 {name => "CgiNavBarLinks"},
147 {name => "CgiStatusHilightColor"},
148 {name => "CgiDateFormatMMDD"},
149 {name => "CgiHeaders"},
150 {name => "CgiExt2ContentType"},
151 {name => "CgiCSSFile"},
153 {text => "CfgEdit_Title_Paths"},
155 {name => "CgiImageDir"},
156 {name => "CgiImageDirURL"},
158 {text => "CfgEdit_Title_User_URLs"},
159 {name => "CgiUserHomePageCheck"},
160 {name => "CgiUserUrlCreate"},
162 {text => "CfgEdit_Title_User_Config_Editing"},
163 {name => "CgiUserConfigEditEnable"},
164 {name => "CgiUserConfigEdit"},
168 text => "CfgEdit_Title_Xfer",
170 {text => "CfgEdit_Title_Xfer_Settings"},
171 {name => "XferMethod", onchangeSubmit => 1},
172 {name => "XferLogLevel"},
173 {name => "ClientCharset"},
174 {name => "ClientCharsetLegacy"},
177 {text => "CfgEdit_Title_Smb_Settings",
178 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
179 {name => "SmbShareName",
180 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
181 {name => "SmbShareUserName",
182 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
183 {name => "SmbSharePasswd",
184 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
187 {text => "CfgEdit_Title_Tar_Settings",
188 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
189 {name => "TarShareName",
190 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
193 {text => "CfgEdit_Title_Rsync_Settings",
194 visible => sub { return $_[0]->{XferMethod} eq "rsync"; } },
195 {text => "CfgEdit_Title_Rsyncd_Settings",
196 visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } },
197 {name => "RsyncShareName",
198 visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } },
199 {name => "RsyncdUserName",
200 visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } },
201 {name => "RsyncdPasswd",
202 visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } },
203 {name => "RsyncdAuthRequired",
204 visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } },
205 {name => "RsyncCsumCacheVerifyProb",
206 visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } },
209 {text => "CfgEdit_Title_Ftp_Settings",
210 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
211 {name => "FtpShareName",
212 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
213 {name => "FtpUserName",
214 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
215 {name => "FtpPasswd",
216 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
217 {name => "FtpBlockSize",
218 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
220 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
221 {name => "FtpTimeout",
222 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
223 {name => "FtpFollowSymlinks",
224 visible => sub { return $_[0]->{XferMethod} eq "ftp"; } },
226 ### BackupPCd Settings
227 {text => "CfgEdit_Title_BackupPCd_Settings",
228 visible => sub { return $_[0]->{XferMethod} eq "backuppcd"; } },
229 {name => "BackupPCdShareName",
230 visible => sub { return $_[0]->{XferMethod} eq "backuppcd"; } },
231 {name => "BackupPCdPath",
232 visible => sub { return $_[0]->{XferMethod} eq "backuppcd"; } },
233 {name => "BackupPCdCmd",
234 visible => sub { return $_[0]->{XferMethod} eq "backuppcd"; } },
235 {name => "BackupPCdRestoreCmd",
236 visible => sub { return $_[0]->{XferMethod} eq "backuppcd"; } },
239 {text => "CfgEdit_Title_Archive_Settings",
240 visible => sub { return $_[0]->{XferMethod} eq "archive"; } },
241 {name => "ArchiveDest",
242 visible => sub { return $_[0]->{XferMethod} eq "archive"; } },
243 {name => "ArchiveComp",
244 visible => sub { return $_[0]->{XferMethod} eq "archive"; } },
245 {name => "ArchivePar",
246 visible => sub { return $_[0]->{XferMethod} eq "archive"; } },
247 {name => "ArchiveSplit",
248 visible => sub { return $_[0]->{XferMethod} eq "archive"; } },
250 ### Include/Exclude Settings
251 {text => "CfgEdit_Title_Include_Exclude",
252 visible => sub { return $_[0]->{XferMethod} ne "archive"; } },
253 {name => "BackupFilesOnly",
254 visible => sub { return $_[0]->{XferMethod} ne "archive"; } },
255 {name => "BackupFilesExclude",
256 visible => sub { return $_[0]->{XferMethod} ne "archive"; } },
258 ### Samba paths and commands
259 {text => "CfgEdit_Title_Smb_Paths_Commands",
260 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
261 {name => "SmbClientPath",
262 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
263 {name => "SmbClientFullCmd",
264 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
265 {name => "SmbClientIncrCmd",
266 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
267 {name => "SmbClientRestoreCmd",
268 visible => sub { return $_[0]->{XferMethod} eq "smb"; } },
270 ### Tar paths and commands
271 {text => "CfgEdit_Title_Tar_Paths_Commands",
272 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
273 {name => "TarClientPath",
274 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
275 {name => "TarClientCmd",
276 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
277 {name => "TarFullArgs",
278 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
279 {name => "TarIncrArgs",
280 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
281 {name => "TarClientRestoreCmd",
282 visible => sub { return $_[0]->{XferMethod} eq "tar"; } },
284 ### Rsync paths and commands
285 {text => "CfgEdit_Title_Rsync_Paths_Commands_Args",
286 visible => sub { return $_[0]->{XferMethod} eq "rsync"; } },
287 {text => "CfgEdit_Title_Rsyncd_Port_Args",
288 visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } },
289 {name => "RsyncClientPath",
290 visible => sub { return $_[0]->{XferMethod} eq "rsync"; } },
291 {name => "RsyncClientCmd",
292 visible => sub { return $_[0]->{XferMethod} eq "rsync"; } },
293 {name => "RsyncClientRestoreCmd",
294 visible => sub { return $_[0]->{XferMethod} eq "rsync"; } },
295 {name => "RsyncdClientPort",
296 visible => sub { return $_[0]->{XferMethod} eq "rsyncd"; } },
297 {name => "RsyncArgs",
298 visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } },
299 {name => "RsyncRestoreArgs",
300 visible => sub { return $_[0]->{XferMethod} =~ /rsync/; } },
302 ### Archive paths and commands
303 {text => "CfgEdit_Title_Archive_Paths_Commands",
304 visible => sub { return $_[0]->{XferMethod} eq "archive"; } },
305 {name => "ArchiveClientCmd",
306 visible => sub { return $_[0]->{XferMethod} eq "archive"; } },
311 text => "CfgEdit_Title_Schedule",
313 {text => "CfgEdit_Title_Full_Backups"},
314 {name => "FullPeriod"},
315 {name => "FullKeepCnt"},
316 {name => "FullKeepCntMin"},
317 {name => "FullAgeMax"},
319 {text => "CfgEdit_Title_Incremental_Backups"},
320 {name => "IncrPeriod"},
321 {name => "IncrKeepCnt"},
322 {name => "IncrKeepCntMin"},
323 {name => "IncrAgeMax"},
324 {name => "IncrLevels"},
325 {name => "IncrFill"},
327 {text => "CfgEdit_Title_Blackouts"},
328 {name => "BackupsDisable"},
329 {name => "BlackoutBadPingLimit"},
330 {name => "BlackoutGoodCnt"},
331 {name => "BlackoutPeriods"},
333 {text => "CfgEdit_Title_Other"},
334 {name => "PartialAgeMax"},
335 {name => "RestoreInfoKeepCnt"},
336 {name => "ArchiveInfoKeepCnt"},
337 {name => "BackupZeroFilesIsFatal"},
341 text => "CfgEdit_Title_Backup_Settings",
343 {text => "CfgEdit_Title_Client_Lookup"},
344 {name => "ClientNameAlias"},
345 {name => "NmbLookupCmd"},
346 {name => "NmbLookupFindHostCmd"},
347 {name => "FixedIPNetBiosNameCheck"},
349 {name => "PingMaxMsec"},
351 {text => "CfgEdit_Title_Other"},
352 {name => "ClientTimeout"},
353 {name => "MaxOldPerPCLogFiles"},
354 {name => "CompressLevel"},
356 {text => "CfgEdit_Title_User_Commands"},
357 {name => "DumpPreUserCmd"},
358 {name => "DumpPostUserCmd"},
359 {name => "DumpPreShareCmd"},
360 {name => "DumpPostShareCmd"},
361 {name => "RestorePreUserCmd"},
362 {name => "RestorePostUserCmd"},
363 {name => "ArchivePreUserCmd"},
364 {name => "ArchivePostUserCmd"},
365 {name => "UserCmdCheckStatus"},
369 text => "CfgEdit_Title_Hosts",
371 {text => "CfgEdit_Title_Hosts"},
373 comment => "CfgEdit_Hosts_Comment"},
380 my $pc_dir = "$TopDir/pc";
381 my($content, $contentHidden, $newConf, $override, $mainConf, $hostConf);
384 my $host = $In{host};
385 my $menu = $In{menu} || "server";
386 my $hosts_path = $Hosts;
387 my $config_path = $host eq "" ? "$TopDir/conf/config.pl"
388 : "$TopDir/pc/$host/config.pl";
390 my $Privileged = CheckPermission($host)
391 && ($PrivAdmin || $Conf{CgiUserConfigEditEnable});
392 my $userHost = 1 if ( defined($host) );
395 if ( !$Privileged ) {
396 ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_edit_config_files}}"));
399 if ( defined($In{menu}) || $In{saveAction} eq "Save" ) {
400 $errors = errorCheck();
403 # If there are errors, then go back to the same menu
405 $In{saveAction} = "";
408 if ( (my $var = $In{overrideUncheck}) ne "" ) {
410 # a compound variable was unchecked; delete or
411 # add extra variables to make the shape the same.
413 #print STDERR Dumper(\%In);
414 foreach my $v ( keys(%In) ) {
415 if ( $v =~ /^v_zZ_(\Q$var\E(_zZ_.*|$))/ ) {
416 delete($In{$v}) if ( !defined($In{"orig_zZ_$1"}) );
418 if ( $v =~ /^orig_zZ_(\Q$var\E(_zZ_.*|$))/ ) {
419 $In{"v_zZ_$1"} = $In{$v};
422 delete($In{"vflds.$var"});
425 ($newConf, $override) = inputParse($bpc, $userHost);
426 $override = undef if ( $host eq "" );
430 # First time: pick up the current config settings
432 $mainConf = $bpc->ConfigDataRead();
434 $hostConf = $bpc->ConfigDataRead($host);
436 foreach my $param ( keys(%$hostConf) ) {
437 $override->{$param} = 1;
440 my $hostInfo = $bpc->HostInfoRead();
442 $mainConf->{Hosts} = [map($hostInfo->{$_}, sort(keys(%$hostInfo)))];
444 $newConf = { %$mainConf, %$hostConf };
447 if ( $In{saveAction} ne "Save" && $In{newMenu} ne ""
448 && defined($ConfigMenu{$In{newMenu}}) ) {
449 $menu = $In{newMenu};
455 # For a non-admin user editing the host config, we need to
456 # figure out which subsets of the menu tree will be visible,
457 # based on what is enabled. Admin users can edit all the
458 # available per-host settings.
460 foreach my $m ( keys(%ConfigMenu) ) {
466 foreach my $paramInfo ( @{$ConfigMenu{$m}{param}} ) {
467 my $param = $paramInfo->{name};
468 if ( defined($paramInfo->{text}) ) {
472 if ( $bpc->{Conf}{CgiUserConfigEdit}{$param}
473 || (defined($bpc->{Conf}{CgiUserConfigEdit}{$param})
475 $mask[$text] = 0 if ( $text >= 0 );
484 $menuDisable{$m}{mask} = \@mask;
485 $menuDisable{$m}{top} = !$enabled;
487 if ( $menuDisable{$menu}{top} ) {
489 # Find an enabled menu if the current menu is empty
491 foreach my $m ( sort(keys(%menuDisable)) ) {
492 if ( !$menuDisable{$m}{top} ) {
501 foreach my $m ( keys(%ConfigMenu) ) {
502 next if ( $menuDisable{$m}{top} );
503 my $text = eval("qq($Lang->{$ConfigMenu{$m}{text}})");
506 <td class="editTabSel"><a href="javascript:menuSubmit('$m')"><b>$text</b></a></td>
510 <td class="editTabNoSel"><a href="javascript:menuSubmit('$m')">$text</a></td>
516 $content .= eval("qq($Lang->{CfgEdit_Header_Main})");
518 $content .= eval("qq($Lang->{CfgEdit_Header_Host})");
522 my $saveColor = "#ff0000";
524 if ( $In{modified} && $In{saveAction} ne "Save" && !%$errors ) {
525 $saveStyle = "style=\"color:$saveColor\"";
531 # Add action and host to the URL so the nav bar link is
534 my $url = "$MyURL?action=editConfig";
535 $url .= "&host=$host" if ( $host ne "" );
537 <table border="0" cellpadding="2">
540 <form method="post" name="editForm" action="$url">
541 <input type="hidden" name="host" value="$host">
542 <input type="hidden" name="menu" value="$menu">
543 <input type="hidden" name="newMenu" value="">
544 <input type="hidden" name="modified" value="$In{modified}">
545 <input type="hidden" name="deleteVar" value="">
546 <input type="hidden" name="insertVar" value="">
547 <input type="hidden" name="overrideUncheck" value="">
548 <input type="hidden" name="addVar" value="">
549 <input type="hidden" name="action" value="editConfig">
550 <input type="hidden" name="saveAction" value="">
551 <input type="button" class="editSaveButton" name="editAction"
552 value="${EscHTML($Lang->{CfgEdit_Button_Save})}" $saveStyle
553 onClick="saveSubmit();">
556 <script language="javascript" type="text/javascript">
559 function saveSubmit()
561 if ( document.editForm.modified.value != 0 ) {
562 document.editForm.saveAction.value = 'Save';
563 document.editForm.submit();
568 function deleteSubmit(varName)
570 document.editForm.deleteVar.value = varName;
571 document.editForm.modified.value = 1;
572 document.editForm.submit();
576 function insertSubmit(varName)
578 document.editForm.insertVar.value = varName;
579 document.editForm.modified.value = 1;
580 document.editForm.submit();
584 function addSubmit(varName, checkKey)
587 && eval("document.editForm.addVarKey_" + varName + ".value") == "" ) {
588 alert("New key must be non-empty");
591 document.editForm.addVar.value = varName;
592 document.editForm.modified.value = 1;
593 document.editForm.submit();
597 function menuSubmit(menuName)
599 document.editForm.newMenu.value = menuName;
600 document.editForm.submit();
603 function varChange(varName)
605 document.editForm.modified.value = 1;
606 document.editForm.editAction.style.color = '$saveColor';
609 function checkboxChange(varName)
611 document.editForm.modified.value = 1;
612 document.editForm.editAction.style.color = '$saveColor';
613 // Do nothing if the checkbox is now set
614 if ( eval("document.editForm.override_" + varName + ".checked") ) {
618 var varRE = new RegExp("^v_zZ_(" + varName + ".*)");
619 var origRE = new RegExp("^orig_zZ_(" + varName + ".*)");
620 for ( var i = 0 ; i < document.editForm.elements.length ; i++ ) {
621 var e = document.editForm.elements[i];
623 if ( (re = varRE.exec(e.name)) != null ) {
624 if ( allVars[re[1]] == null ) {
628 //debugMsg("found v_zZ_ match with " + re[1]);
629 //debugMsg("allVars[" + re[1] + "] = " + allVars[re[1]]);
630 } else if ( (re = origRE.exec(e.name)) != null ) {
631 if ( allVars[re[1]] == null ) {
635 //debugMsg("allVars[" + re[1] + "] = " + allVars[re[1]]);
639 for ( v in allVars ) {
640 if ( allVars[v] != 0 ) {
641 //debugMsg("Not the same shape because of " + v);
644 // copy the original variable values
645 //debugMsg("setting " + v);
646 eval("document.editForm.v_zZ_" + v + ".value = document.editForm.orig_zZ_" + v + ".value");
652 // need to rebuild the form since the compound variable
654 document.editForm.overrideUncheck.value = varName;
655 document.editForm.submit();
660 function checkboxSet(varName)
662 document.editForm.modified.value = 1;
663 document.editForm.editAction.style.color = '$saveColor';
664 eval("document.editForm.override_" + varName + ".checked = 1;");
668 var debugCounter = 0;
669 function debugMsg(msg)
672 var t = document.createTextNode(debugCounter + ": " + msg);
673 var br = document.createElement("br");
674 var debug = document.getElementById("debug");
675 debug.appendChild(t);
676 debug.appendChild(br);
679 function displayHelp(varName)
681 var help = document.getElementById("id_" + varName);
682 help.style.display = help.style.display == "block" ? "none" : "block";
688 <span id="debug">$debugText</span>
693 <table border="1" cellspacing="0">
700 # There is a special case of the user deleting just the field
701 # that has the error(s). So if the delete variable is a match
702 # or parent to all the errors then ignore the errors.
704 if ( $In{deleteVar} ne "" && %$errors > 0 ) {
706 foreach my $v ( keys(%$errors) ) {
707 if ( $v ne $In{deleteVar} && $v !~ /^\Q$In{deleteVar}_zZ_/ ) {
712 $errors = {} if ( $matchAll );
715 my $isError = %$errors;
717 if ( !$isError && $In{saveAction} eq "Save" ) {
720 $hostConf = $bpc->ConfigDataRead($host) if ( !defined($hostConf) );
721 my %hostConf2 = %$hostConf;
722 foreach my $param ( keys(%$newConf) ) {
723 if ( $override->{$param} ) {
724 $hostConf->{$param} = $newConf->{$param}
726 delete($hostConf->{$param});
729 $mesg = configDiffMesg($host, \%hostConf2, $hostConf);
730 $err .= $bpc->ConfigDataWrite($host, $hostConf);
732 $mainConf = $bpc->ConfigDataRead() if ( !defined($mainConf) );
735 my($hostsNew, $allHosts, $copyConf);
736 foreach my $entry ( @{$newConf->{Hosts}} ) {
737 next if ( $entry->{host} eq "" );
738 $allHosts->{$entry->{host}} = 1;
739 $allHosts->{$1} = 1 if ( $entry->{host} =~ /(.+?)\s*=/ );
741 foreach my $entry ( @{$newConf->{Hosts}} ) {
742 next if ( $entry->{host} eq ""
743 || defined($hostsNew->{$entry->{host}}) );
744 if ( $entry->{host} =~ /(.+?)\s*=\s*(.+)/ ) {
745 if ( defined($allHosts->{$2}) ) {
747 $copyConf->{$1} = $2;
749 my $fullHost = $entry->{host};
751 $err .= eval("qq($Lang->{CfgEdit_Error_Copy_host_does_not_exist})");
754 push(@$hostsSave, $entry);
755 $hostsNew->{$entry->{host}} = $entry;
757 ($mesg, my $hostChange) = hostsDiffMesg($hostsNew);
758 $bpc->HostInfoWrite($hostsNew) if ( $hostChange );
759 foreach my $host ( keys(%$copyConf) ) {
761 # Currently host names are forced to lc when they
762 # are read from the hosts file. Therefore we need
763 # to force the from and to hosts to lc.
765 my $confData = $bpc->ConfigDataRead(lc($copyConf->{$host}));
766 my $fromHost = $copyConf->{$host};
767 $err .= $bpc->ConfigDataWrite(lc($host), $confData);
768 $mesg .= eval("qq($Lang->{CfgEdit_Log_Copy_host_config})");
771 delete($newConf->{Hosts});
772 $mesg .= configDiffMesg(undef, $mainConf, $newConf);
773 $mainConf = { %$mainConf, %$newConf };
774 $err .= $bpc->ConfigDataWrite(undef, $mainConf);
775 $newConf->{Hosts} = $hostsSave;
777 if ( defined($err) ) {
778 $tblContent .= <<EOF;
779 <tr><td colspan="2" class="border"><span class="editError">$err</span></td></tr>
782 $bpc->ServerConnect();
784 (my $mesgBR = $mesg) =~ s/\n/<br>\n/g;
785 # uncomment this if you want the changes to be displayed
786 # $tblContent .= <<EOF;
787 #<tr><td colspan="2" class="border"><span class="editComment">$mesgBR</span></td></tr>
789 foreach my $str ( split(/\n/, $mesg) ) {
790 $bpc->ServerMesg("log $str") if ( $str ne "" );
794 # Tell the server to reload, unless we only changed
797 $bpc->ServerMesg("server reload") if ( $host eq "" );
800 my @mask = @{$menuDisable{$menu}{mask} || []};
802 foreach my $paramInfo ( @{$ConfigMenu{$menu}{param}} ) {
804 my $param = $paramInfo->{name};
805 my $disabled = shift(@mask);
807 next if ( $disabled || $menuDisable{$menu}{top} );
808 if ( ref($paramInfo->{visible}) eq "CODE"
809 && !&{$paramInfo->{visible}}($newConf, $bpc) ) {
813 if ( defined($paramInfo->{text}) ) {
814 my $text = eval("qq($Lang->{$paramInfo->{text}})");
815 $tblContent .= <<EOF;
816 <tr><td colspan="2" class="editHeader">$text</td></tr>
822 # TODO: get parameter documentation
825 #$comment =~ s/\'//g;
826 #$comment =~ s/\"//g;
827 #$comment =~ s/\n/ /g;
829 $doneParam->{$param} = 1;
831 $tblContent .= fieldEditBuild($ConfigMeta{$param},
838 $paramInfo->{onchangeSubmit},
839 defined($override) ? $param : undef,
840 defined($override) ? $override->{$param} : undef
842 if ( defined($paramInfo->{comment}) ) {
843 my $topDir = $bpc->TopDir;
844 my $text = eval("qq($Lang->{$paramInfo->{comment}})");
845 $tblContent .= <<EOF;
846 <tr><td colspan="2" class="editComment">$text</td></tr>
852 # Emit a summary of all the errors
858 <tr><td colspan="2" class="border"><span class="editError">$Lang->{CfgEdit_Error_No_Save}</span></td></tr>
862 foreach my $param ( sort(keys(%$errors)) ) {
864 <tr><td colspan="2" class="border"><span class="editError">$errors->{$param}</span></td></tr>
875 # Emit all the remaining editable config settings as hidden values
877 foreach my $param ( keys(%ConfigMeta) ) {
878 next if ( $doneParam->{$param} );
880 && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param})
882 && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) );
883 $content .= fieldHiddenBuild($ConfigMeta{$param},
888 if ( defined($override) ) {
890 <input type="hidden" name="override_$param" value="$override->{$param}">
893 $doneParam->{$param} = 1;
896 if ( defined($In{menu}) || $In{saveAction} eq "Save" ) {
897 if ( $In{saveAction} eq "Save" && !$userHost ) {
899 # Emit the new settings as orig_zZ_ parameters
902 foreach my $param ( keys(%ConfigMeta) ) {
903 next if ( $doneParam->{$param} );
905 && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param})
907 && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) );
908 $contentHidden .= fieldHiddenBuild($ConfigMeta{$param},
913 $doneParam->{$param} = 1;
918 # Just switching menus: copy all the orig_zZ_ input parameters
920 foreach my $var ( keys(%In) ) {
921 next if ( $var !~ /^orig_zZ_/ );
922 my $val = decode_utf8($In{$var});
923 $contentHidden .= <<EOF;
924 <input type="hidden" name="$var" value="${EscHTML($val)}">
930 # First time: emit all the original config settings
933 foreach my $param ( keys(%ConfigMeta) ) {
934 next if ( $doneParam->{$param} );
936 && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param})
938 && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) );
939 $contentHidden .= fieldHiddenBuild($ConfigMeta{$param},
944 $doneParam->{$param} = 1;
955 Header("Config Edit", $content);
961 my($type, $varName, $varValue, $prefix) = @_;
964 $type = { type => $type } if ( ref($type) ne "HASH" );
966 if ( $type->{type} eq "list" ) {
967 $varValue = [] if ( !defined($varValue) );
968 $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" );
970 for ( my $i = 0 ; $i < @$varValue ; $i++ ) {
971 $content .= fieldHiddenBuild($type->{child}, "${varName}_zZ_$i",
972 $varValue->[$i], $prefix);
974 } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) {
975 $varValue = {} if ( ref($varValue) ne "HASH" );
976 my(@order, $childType);
978 if ( defined($type->{order}) ) {
979 @order = @{$type->{order}};
980 } elsif ( defined($type->{child}) ) {
981 @order = sort(keys(%{$type->{child}}));
983 @order = sort(keys(%$varValue));
986 foreach my $fld ( @order ) {
987 if ( defined($type->{child}) ) {
988 $childType = $type->{child}{$fld};
990 $childType = $type->{childType};
992 # emit list of fields since they are user-defined
993 # rather than hard-coded
996 <input type="hidden" name="vflds.$varName" value="${EscHTML($fld)}">
999 $content .= fieldHiddenBuild($childType, "${varName}_zZ_$fld",
1000 $varValue->{$fld}, $prefix);
1002 } elsif ( $type->{type} eq "shortlist" ) {
1003 $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" );
1004 $varValue = join(", ", @$varValue);
1006 <input type="hidden" name="${prefix}_zZ_$varName" value="${EscHTML($varValue)}">
1010 <input type="hidden" name="${prefix}_zZ_$varName" value="${EscHTML($varValue)}">
1018 my($type, $varName, $varValue, $errors, $level, $comment, $isError,
1019 $onchangeSubmit, $overrideVar, $overrideSet) = @_;
1022 my $size = 50 - 10 * $level;
1023 $type = { type => $type } if ( ref($type) ne "HASH" );
1025 $size = $type->{size} if ( defined($type->{size}) );
1028 # These fragments allow inline content to be turned on and off
1030 # <tr><td colspan="2"><span id="id_$varName" style="display: none" class="editComment">$comment</span></td></tr>
1031 # <tr><td class="border"><a href="javascript: displayHelp('$varName')">$varName</a>
1034 if ( $level == 0 ) {
1035 my $lcVarName = lc($varName);
1037 <tr><td class="border"><a href="?action=view&type=docs#item__conf_${lcVarName}_">$varName</a>
1039 if ( defined($overrideVar) ) {
1040 my $override_checked = "";
1041 if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_zZ_/
1042 || !$isError && $In{insertVar} =~ /^\Q${varName}\E(_zZ_|$)/
1043 || !$isError && $In{addVar} =~ /^\Q${varName}\E(_zZ_|$)/ ) {
1046 if ( $overrideSet ) {
1047 $override_checked = "checked";
1050 <br><input type="checkbox" name="override_$varName" $override_checked value="1" onClick="checkboxChange('$varName')">\ ${EscHTML($Lang->{CfgEdit_Button_Override})}
1053 $content .= "</td>\n";
1056 if ( $type->{type} eq "list" ) {
1057 $content .= "<td class=\"border\">\n";
1058 $varValue = [] if ( !defined($varValue) );
1059 $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" );
1060 if ( !$isError && $In{deleteVar} =~ /^\Q${varName}_zZ_\E(\d+)$/
1061 && $1 < @$varValue ) {
1063 # User deleted entry in this array
1065 splice(@$varValue, $1, 1) if ( @$varValue > 1 || $type->{emptyOk} );
1066 $In{deleteVar} = "";
1068 if ( !$isError && $In{insertVar} =~ /^\Q${varName}_zZ_\E(\d+)$/
1069 && $1 < @$varValue ) {
1071 # User inserted entry in this array
1073 splice(@$varValue, $1, 0, "")
1074 if ( @$varValue > 1 || $type->{emptyOk} );
1075 $In{insertVar} = "";
1077 if ( !$isError && $In{addVar} eq $varName ) {
1079 # User added entry to this array
1081 push(@$varValue, undef);
1084 $content .= "<table border=\"1\" cellspacing=\"0\">\n";
1087 if ( ref($type) eq "HASH" && ref($type->{child}) eq "HASH"
1088 && $type->{child}{type} eq "horizHash" ) {
1090 if ( defined($type->{child}{order}) ) {
1091 @order = @{$type->{child}{order}};
1093 @order = sort(keys(%{$type->{child}{child}}));
1095 $content .= "<tr><td class=\"border\"></td>\n";
1096 for ( my $i = 0 ; $i < @order ; $i++ ) {
1097 $content .= "<td class=\"tableheader\">$order[$i]</td>\n";
1099 $colspan = @order + 1;
1100 $content .= "</tr>\n";
1101 for ( my $i = 0 ; $i < @$varValue ; $i++ ) {
1102 if ( @$varValue > 1 || $type->{emptyOk} ) {
1105 <input type="button" name="del_${varName}_zZ_$i" value="${EscHTML($Lang->{CfgEdit_Button_Delete})}"
1106 onClick="deleteSubmit('${varName}_zZ_$i')">
1110 $content .= fieldEditBuild($type->{child}, "${varName}_zZ_$i",
1111 $varValue->[$i], $errors, $level + 1, undef,
1112 $isError, $onchangeSubmit,
1113 $overrideVar, $overrideSet);
1114 $content .= "</tr>\n";
1117 for ( my $i = 0 ; $i < @$varValue ; $i++ ) {
1119 <tr><td class="border">
1120 <input type="button" name="ins_${varName}_zZ_$i" value="${EscHTML($Lang->{CfgEdit_Button_Insert})}"
1121 onClick="insertSubmit('${varName}_zZ_$i')">
1123 if ( @$varValue > 1 || $type->{emptyOk} ) {
1125 <input type="button" name="del_${varName}_zZ_$i" value="${EscHTML($Lang->{CfgEdit_Button_Delete})}"
1126 onClick="deleteSubmit('${varName}_zZ_$i')">
1129 $content .= "</td>\n";
1130 $content .= fieldEditBuild($type->{child}, "${varName}_zZ_$i",
1131 $varValue->[$i], $errors, $level + 1, undef,
1132 $isError, $onchangeSubmit,
1133 $overrideVar, $overrideSet);
1134 $content .= "</tr>\n";
1139 <tr><td class="border" colspan="$colspan"><input type="button" name="add_$varName" value="${EscHTML($Lang->{CfgEdit_Button_Add})}"
1140 onClick="addSubmit('$varName')"></td></tr>
1143 $content .= "</td>\n";
1144 } elsif ( $type->{type} eq "hash" ) {
1145 $content .= "<td class=\"border\">\n";
1146 $content .= "<table border=\"1\" cellspacing=\"0\">\n";
1147 $varValue = {} if ( ref($varValue) ne "HASH" );
1149 if ( !$isError && !$type->{noKeyEdit}
1150 && $In{deleteVar} !~ /^\Q${varName}_zZ_\E.*_zZ_/
1151 && $In{deleteVar} =~ /^\Q${varName}_zZ_\E(.*)$/ ) {
1153 # User deleted entry in this hash
1155 delete($varValue->{$1}) if ( keys(%$varValue) > 1
1156 || $type->{emptyOk} );
1157 $In{deleteVar} = "";
1159 if ( !$isError && !defined($type->{child})
1160 && $In{addVar} eq $varName ) {
1162 # User added entry to this array
1164 $varValue->{$In{"addVarKey_$varName"}} = ""
1165 if ( !defined($varValue->{$In{"addVarKey_$varName"}}) );
1168 my(@order, $childType);
1170 if ( defined($type->{order}) ) {
1171 @order = @{$type->{order}};
1172 } elsif ( defined($type->{child}) ) {
1173 @order = sort(keys(%{$type->{child}}));
1175 @order = sort(keys(%$varValue));
1178 foreach my $fld ( @order ) {
1180 <tr><td class="border">$fld
1182 if ( !$type->{noKeyEdit}
1183 && (keys(%$varValue) > 1 || $type->{emptyOk}) ) {
1185 <input type="submit" name="del_${varName}_zZ_$fld" value="${EscHTML($Lang->{CfgEdit_Button_Delete})}"
1186 onClick="deleteSubmit('${varName}_zZ_$fld')">
1189 if ( defined($type->{child}) ) {
1190 $childType = $type->{child}{$fld};
1192 $childType = $type->{childType};
1194 # emit list of fields since they are user-defined
1195 # rather than hard-coded
1198 <input type="hidden" name="vflds.$varName" value="${EscHTML($fld)}">
1201 $content .= "</td>\n";
1202 $content .= fieldEditBuild($childType, "${varName}_zZ_$fld",
1203 $varValue->{$fld}, $errors, $level + 1, undef,
1204 $isError, $onchangeSubmit,
1205 $overrideVar, $overrideSet);
1206 $content .= "</tr>\n";
1209 if ( !$type->{noKeyEdit} ) {
1211 <tr><td class="border" colspan="2">
1212 $Lang->{CfgEdit_Button_New_Key}: <input type="text" class="editTextInput" name="addVarKey_$varName" size="20" maxlength="256" value="">
1213 <input type="button" name="add_$varName" value="${EscHTML($Lang->{CfgEdit_Button_Add})}" onClick="addSubmit('$varName', 1)">
1217 $content .= "</table>\n";
1218 $content .= "</td>\n";
1219 } elsif ( $type->{type} eq "horizHash" ) {
1220 $varValue = {} if ( ref($varValue) ne "HASH" );
1221 my(@order, $childType);
1223 if ( defined($type->{order}) ) {
1224 @order = @{$type->{order}};
1225 } elsif ( defined($type->{child}) ) {
1226 @order = sort(keys(%{$type->{child}}));
1228 @order = sort(keys(%$varValue));
1231 foreach my $fld ( @order ) {
1232 if ( defined($type->{child}) ) {
1233 $childType = $type->{child}{$fld};
1235 $childType = $type->{childType};
1237 # emit list of fields since they are user-defined
1238 # rather than hard-coded
1241 <input type="hidden" name="vflds.$varName" value="${EscHTML($fld)}">
1244 $content .= fieldEditBuild($childType, "${varName}_zZ_$fld",
1245 $varValue->{$fld}, $errors, $level + 1, undef,
1246 $isError, $onchangeSubmit,
1247 $overrideVar, $overrideSet);
1250 $content .= "<td class=\"border\">\n";
1253 # If there was an error, we use the original post values
1254 # in %In, rather than the parsed values in $varValue.
1255 # This is so that the user's erroneous input is preserved.
1257 $varValue = $In{"v_zZ_$varName"} if ( defined($In{"v_zZ_$varName"}) );
1259 if ( defined($errors->{$varName}) ) {
1261 <span class="editError">$errors->{$varName}</span><br>
1265 if ( defined($overrideVar) ) {
1266 $onChange .= "checkboxSet('$overrideVar');";
1268 $onChange .= "varChange('$varName');";
1270 if ( $onchangeSubmit ) {
1271 $onChange .= "document.editForm.submit();";
1273 if ( $onChange ne "" ) {
1274 $onChange = " onChange=\"$onChange\"";
1276 if ( $varValue !~ /\n/ &&
1277 ($type->{type} eq "integer"
1278 || $type->{type} eq "string"
1279 || $type->{type} eq "execPath"
1280 || $type->{type} eq "shortlist"
1281 || $type->{type} eq "float") ) {
1283 if ( $type->{type} eq "shortlist" ) {
1284 $varValue = [$varValue] if ( ref($varValue) ne "ARRAY" );
1285 $varValue = join(", ", @$varValue);
1287 my $textType = ($varName =~ /Passwd/) ? "password" : "text";
1289 <input type="$textType" class="editTextInput" name="v_zZ_$varName" size="$size" maxlength="256" value="${EscHTML($varValue)}"$onChange>
1291 } elsif ( $type->{type} eq "boolean" ) {
1293 my $checked = "checked" if ( $varValue );
1295 <input type="checkbox" name="v_zZ_$varName" $checked value="1"$onChange>
1297 } elsif ( $type->{type} eq "select" ) {
1299 <select name="v_zZ_$varName"$onChange>
1301 foreach my $option ( @{$type->{values}} ) {
1302 my $sel = " selected" if ( $varValue eq $option );
1303 $content .= "<option$sel>$option</option>\n";
1305 $content .= "</select>\n";
1307 # multi-line text area - count number of lines
1308 my $rowCnt = $varValue =~ tr/\n//;
1309 $rowCnt = 1 if ( $rowCnt < 1 );
1311 <textarea name="v_zZ_$varName" class="editTextArea" cols="$size" rows="$rowCnt"$onChange>${EscHTML($varValue)}</textarea>
1314 $content .= "</td>\n";
1323 foreach my $param ( keys(%ConfigMeta) ) {
1324 fieldErrorCheck($ConfigMeta{$param}, $param, $errors);
1331 my($type, $varName, $errors) = @_;
1333 $type = { type => $type } if ( ref($type) ne "HASH" );
1335 if ( $type->{type} eq "list" ) {
1336 for ( my $i = 0 ; ; $i++ ) {
1337 last if ( fieldErrorCheck($type->{child}, "${varName}_zZ_$i", $errors) );
1339 } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) {
1340 my(@order, $childType);
1343 if ( defined($type->{order}) ) {
1344 @order = @{$type->{order}};
1345 } elsif ( defined($type->{child}) ) {
1346 @order = sort(keys(%{$type->{child}}));
1348 @order = split(/\0/, $In{"vflds.$varName"});
1350 foreach my $fld ( @order ) {
1351 if ( defined($type->{child}) ) {
1352 $childType = $type->{child}{$fld};
1354 $childType = $type->{childType};
1356 $ret ||= fieldErrorCheck($childType, "${varName}_zZ_$fld", $errors);
1360 $In{"v_zZ_$varName"} = "0" if ( $type->{type} eq "boolean"
1361 && $In{"v_zZ_$varName"} eq "" );
1363 return 1 if ( !exists($In{"v_zZ_$varName"}) );
1365 (my $var = $varName) =~ s/_zZ_/./g;
1367 if ( $type->{type} eq "integer"
1368 || $type->{type} eq "boolean" ) {
1369 if ( $In{"v_zZ_$varName"} !~ /^-?\d+\s*$/s
1370 && $In{"v_zZ_$varName"} ne "" ) {
1371 $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_an_integer}}");
1373 } elsif ( $type->{type} eq "float" ) {
1374 if ( $In{"v_zZ_$varName"} !~ /^-?\d*(\.\d*)?\s*$/s
1375 && $In{"v_zZ_$varName"} ne "" ) {
1377 = eval("qq{$Lang->{CfgEdit_Error__must_be_real_valued_number}}");
1379 } elsif ( $type->{type} eq "shortlist" ) {
1380 my @vals = split(/[,\s]+/, $In{"v_zZ_$varName"});
1381 for ( my $i = 0 ; $i < @vals ; $i++ ) {
1382 if ( $type->{child} eq "integer"
1383 && $vals[$i] !~ /^-?\d+\s*$/s
1384 && $vals[$i] ne "" ) {
1386 $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__entry__must_be_an_integer}}");
1387 } elsif ( $type->{child} eq "float"
1388 && $vals[$i] !~ /^-?\d*(\.\d*)?\s*$/s
1389 && $vals[$i] ne "" ) {
1391 $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__entry__must_be_real_valued_number}}");
1394 } elsif ( $type->{type} eq "select" ) {
1396 foreach my $option ( @{$type->{values}} ) {
1397 if ( $In{"v_zZ_$varName"} eq $option ) {
1402 $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_valid_option}}")
1404 } elsif ( $type->{type} eq "execPath" ) {
1405 if ( $In{"v_zZ_$varName"} ne "" && !-x $In{"v_zZ_$varName"} ) {
1406 $errors->{$varName} = eval("qq{$Lang->{CfgEdit_Error__must_be_executable_program}}");
1410 # $type->{type} eq "string": no error checking
1419 my($bpc, $userHost) = @_;
1423 foreach my $param ( keys(%ConfigMeta) ) {
1426 && (!defined($bpc->{Conf}{CgiUserConfigEdit}{$param})
1428 && !$bpc->{Conf}{CgiUserConfigEdit}{$param})) );
1429 fieldInputParse($ConfigMeta{$param}, $param, \$value);
1430 $conf->{$param} = $value;
1431 $override->{$param} = $In{"override_$param"};
1433 return ($conf, $override);
1438 my($type, $varName, $value) = @_;
1440 $type = { type => $type } if ( ref($type) ne "HASH" );
1442 if ( $type->{type} eq "list" ) {
1444 for ( my $i = 0 ; ; $i++ ) {
1446 last if ( fieldInputParse($type->{child}, "${varName}_zZ_$i", \$val) );
1447 push(@$$value, $val);
1449 $$value = undef if ( $type->{undefIfEmpty} && @$$value == 0 );
1450 } elsif ( $type->{type} eq "hash" || $type->{type} eq "horizHash" ) {
1451 my(@order, $childType);
1455 if ( defined($type->{order}) ) {
1456 @order = @{$type->{order}};
1457 } elsif ( defined($type->{child}) ) {
1458 @order = sort(keys(%{$type->{child}}));
1460 @order = split(/\0/, $In{"vflds.$varName"});
1463 foreach my $fld ( @order ) {
1465 if ( defined($type->{child}) ) {
1466 $childType = $type->{child}{$fld};
1468 $childType = $type->{childType};
1470 $ret ||= fieldInputParse($childType, "${varName}_zZ_$fld", \$val);
1472 $$value->{$fld} = $val;
1476 if ( $type->{type} eq "boolean" ) {
1477 $$value = 0 + $In{"v_zZ_$varName"};
1478 } elsif ( !exists($In{"v_zZ_$varName"}) ) {
1482 my $v = $In{"v_zZ_$varName"};
1484 if ( $type->{type} eq "integer" ) {
1485 if ( $v =~ /^-?\d+\s*$/s || $v eq "" ) {
1488 # error value - keep in string form
1491 } elsif ( $type->{type} eq "float" ) {
1492 if ( $v =~ /^-?\d*(\.\d*)?\s*$/s || $v eq "" ) {
1495 # error value - keep in string form
1498 } elsif ( $type->{type} eq "shortlist" ) {
1499 $$value = [split(/[,\s]+/, $v)];
1500 if ( $type->{child} eq "float" ) {
1501 foreach ( @$$value ) {
1502 if ( /^-?\d*(\.\d*)?\s*$/s || $v eq "" ) {
1506 } elsif ( $type->{child} eq "integer"
1507 || $type->{child} eq "boolean" ) {
1508 foreach ( @$$value ) {
1509 if ( /^-?\d+\s*$/s || $v eq "" ) {
1515 $$value = decode_utf8($In{"v_zZ_$varName"});
1516 $$value =~ s/\r\n/\n/g;
1517 # remove leading space from exec paths
1518 $$value =~ s/^\s+// if ( $type->{type} eq "execPath" );
1520 $$value = undef if ( $type->{undefIfEmpty} && $$value eq "" );
1527 my($host, $oldConf, $newConf) = @_;
1531 if ( $host ne "" ) {
1532 $conf = "host $host config";
1534 $conf = "main config";
1537 foreach my $p ( keys(%ConfigMeta) ) {
1538 if ( !exists($oldConf->{$p}) && !exists($newConf->{$p}) ) {
1540 } elsif ( exists($oldConf->{$p}) && !exists($newConf->{$p}) ) {
1541 $mesg .= eval("qq($Lang->{CfgEdit_Log_Delete_param})");
1542 } elsif ( !exists($oldConf->{$p}) && exists($newConf->{$p}) ) {
1543 my $dump = Data::Dumper->new([$newConf->{$p}]);
1547 my $value = $dump->Dump;
1548 $value =~ s/\n/\\n/g;
1549 $value =~ s/\r/\\r/g;
1550 $mesg .= eval("qq($Lang->{CfgEdit_Log_Add_param_value})");
1552 my $dump = Data::Dumper->new([$newConf->{$p}]);
1556 my $valueNew = $dump->Dump;
1558 my $v = $oldConf->{$p};
1559 if ( ref($newConf->{$p}) eq "ARRAY" && ref($v) eq "" ) {
1562 $dump = Data::Dumper->new([$v]);
1566 my $valueOld = $dump->Dump;
1568 (my $valueNew2 = $valueNew) =~ s/['\n\r]//g;
1569 (my $valueOld2 = $valueOld) =~ s/['\n\r]//g;
1571 next if ( $valueOld2 eq $valueNew2 );
1573 $valueNew =~ s/\n/\\n/g;
1574 $valueOld =~ s/\n/\\n/g;
1575 $valueNew =~ s/\r/\\r/g;
1576 $valueOld =~ s/\r/\\r/g;
1578 $mesg .= eval("qq($Lang->{CfgEdit_Log_Change_param_value})");
1587 my $hostsOld = $bpc->HostInfoRead();
1588 my($mesg, $hostChange);
1590 foreach my $host ( keys(%$hostsOld) ) {
1591 if ( !defined($hostsNew->{$host}) ) {
1592 $mesg .= eval("qq($Lang->{CfgEdit_Log_Host_Delete})");
1596 foreach my $key ( keys(%{$hostsNew->{$host}}) ) {
1597 next if ( $hostsNew->{$host}{$key} eq $hostsOld->{$host}{$key} );
1598 my $valueOld = $hostsOld->{$host}{$key};
1599 my $valueNew = $hostsNew->{$host}{$key};
1600 $mesg .= eval("qq($Lang->{CfgEdit_Log_Host_Change})");
1605 foreach my $host ( keys(%$hostsNew) ) {
1606 next if ( defined($hostsOld->{$host}) );
1607 my $dump = Data::Dumper->new([$hostsNew->{$host}]);
1611 my $value = $dump->Dump;
1612 $value =~ s/\n/\\n/g;
1613 $value =~ s/\r/\\r/g;
1614 $mesg .= eval("qq($Lang->{CfgEdit_Log_Host_Add})");
1617 return ($mesg, $hostChange);