Hello community,
here is the log from the commit of package yast2-samba-client
checked in at Tue Apr 18 17:33:04 CEST 2006.
--------
--- yast2-samba-client/yast2-samba-client.changes 2006-03-31 17:19:03.000000000 +0200
+++ NOARCH/yast2-samba-client/yast2-samba-client.changes 2006-04-18 17:09:49.000000000 +0200
@@ -1,0 +2,6 @@
+Thu Apr 13 14:31:41 CEST 2006 - jsuchome@suse.cz
+
+- use /etc/security/pam_winbind.conf for winbind config (#165775)
+- 2.13.15
+
+-------------------------------------------------------------------
Old:
----
yast2-samba-client-2.13.14.tar.bz2
New:
----
yast2-samba-client-2.13.15.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ yast2-samba-client.spec ++++++
--- /var/tmp/diff_new_pack.3HQDrE/_old 2006-04-18 17:32:57.000000000 +0200
+++ /var/tmp/diff_new_pack.3HQDrE/_new 2006-04-18 17:32:57.000000000 +0200
@@ -1,5 +1,5 @@
#
-# spec file for package yast2-samba-client (Version 2.13.14)
+# spec file for package yast2-samba-client (Version 2.13.15)
#
# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany.
# This file and all modifications and additions to the pristine
@@ -11,12 +11,12 @@
# norootforbuild
Name: yast2-samba-client
-Version: 2.13.14
+Version: 2.13.15
Release: 1
License: GPL
Group: System/YaST
BuildRoot: %{_tmppath}/%{name}-%{version}-build
-Source0: yast2-samba-client-2.13.14.tar.bz2
+Source0: yast2-samba-client-2.13.15.tar.bz2
prefix: /usr
BuildRequires: perl-XML-Writer update-desktop-files yast2-devtools yast2-pam yast2-perl-bindings yast2-testsuite
Requires: yast2 yast2-pam yast2-kerberos-client
@@ -34,7 +34,7 @@
Stanislav Visnovsky
%prep
-%setup -n yast2-samba-client-2.13.14
+%setup -n yast2-samba-client-2.13.15
%build
%{prefix}/bin/y2tool y2autoconf
@@ -71,6 +71,9 @@
%doc %{prefix}/share/doc/packages/yast2-samba-client
%changelog -n yast2-samba-client
+* Thu Apr 13 2006 - jsuchome@suse.cz
+- use /etc/security/pam_winbind.conf for winbind config (#165775)
+- 2.13.15
* Wed Mar 29 2006 - jsuchome@suse.cz
- fixed dig call (#158726)
- 2.13.14
++++++ yast2-samba-client-2.13.14.tar.bz2 -> yast2-samba-client-2.13.15.tar.bz2 ++++++
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/VERSION new/yast2-samba-client-2.13.15/VERSION
--- old/yast2-samba-client-2.13.14/VERSION 2006-03-29 20:03:58.000000000 +0200
+++ new/yast2-samba-client-2.13.15/VERSION 2006-04-13 14:32:18.000000000 +0200
@@ -1 +1 @@
-2.13.14
+2.13.15
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/agents/Makefile.am new/yast2-samba-client-2.13.15/agents/Makefile.am
--- old/yast2-samba-client-2.13.14/agents/Makefile.am 2005-12-19 10:49:04.000000000 +0100
+++ new/yast2-samba-client-2.13.15/agents/Makefile.am 2006-04-13 13:51:53.000000000 +0200
@@ -2,6 +2,6 @@
# Makefile.am for devtools/devtools/skeletons/config/agents
#
-scrconf_DATA = cfg_smbconf.scr
+scrconf_DATA = cfg_smbconf.scr etc_security_winbind.scr
EXTRA_DIST = $(scrconf_DATA)
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/agents/Makefile.in new/yast2-samba-client-2.13.15/agents/Makefile.in
--- old/yast2-samba-client-2.13.14/agents/Makefile.in 2006-02-02 19:28:24.000000000 +0100
+++ new/yast2-samba-client-2.13.15/agents/Makefile.in 2006-04-13 14:11:41.000000000 +0200
@@ -165,7 +165,7 @@
ydatadir = @ydatadir@
yncludedir = @yncludedir@
ystartupdir = @ystartupdir@
-scrconf_DATA = cfg_smbconf.scr
+scrconf_DATA = cfg_smbconf.scr etc_security_winbind.scr
EXTRA_DIST = $(scrconf_DATA)
all: all-am
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/agents/etc_security_winbind.scr new/yast2-samba-client-2.13.15/agents/etc_security_winbind.scr
--- old/yast2-samba-client-2.13.14/agents/etc_security_winbind.scr 1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-samba-client-2.13.15/agents/etc_security_winbind.scr 2006-04-13 13:52:52.000000000 +0200
@@ -0,0 +1,43 @@
+#
+
+.etc.security_winbind
+
+`ag_ini(
+ `IniAgent("/etc/security/pam_winbind.conf", $[
+ "options" : [ "no_nested_sections", "ignore_case", "line_can_continue", "comments_last", "repeat_names" ],
+ "comments": [
+ "^[ \t]*$", // empty line
+ "^[ \t]+[;#].*$", // comment char is not first char
+ "^[;#][ \t]*$", // only comment chars
+ "^[;#][ \t]*\\[[^]]*$", // comment chars followed by '[' without matching ']'
+ "^[;#][^ \t[]", // comment char followed by non-blank nor '['
+ "^[;#][ \t]+[^[a-z \t].*$", // comment chars followed by non a-z char nor '[' nor blank
+ "^[;#][ \t]+[a-z ]*[a-z][ \t]*$", // comment chars followed by a-z or blank chars
+ "^[;#][ \t]+[a-z ]*[a-z][ \t]*[^a-z \t=].*$", // like above, but followed by non a-z nor blank nor '=' chars
+ ],
+ "sections" : [
+ $[
+ "begin" : [ "^[ \t]*\\[[ \t]*(.*[^ \t])[ \t]*\\][ \t]*", "[%s]" ],
+ ], $[
+ // this is a special type for commenting out the values
+ "begin" : [ "^[#;][ \t]*\\[[ \t]*(.*[^ \t])[ \t]*\\][ \t]*", "# [%s]" ],
+ ]
+ ],
+ // we need to exclude ; because of the second matching rule
+ "params" : [
+ $[
+ "match" : [ "^[ \t]*([a-z:_ ]*[a-z])[ \t]*=[ \t]*(.*[^ \t])[ \t]*$" , "\t%s = %s"],
+ ], $[
+ // this is a special type for commenting out the values
+// "match" : [ "^[;#]+[ \t]*([a-z ]*[a-z])[ \t]*=[ \t]*(.*[^ \t])[ \t]*$" , "#\t%s = %s"],
+ "match" : [ "^[#;][ \t]+([a-z ]*[a-z])[ \t]*=[ \t]*(.*[^ \t])[ \t]*$" , "#\t%s = %s"],
+ ], $[
+ // rule only for writting
+ // comment key/value pairs with '##' instead of one '#' used for disabled items
+ "match" : [ "^# FAKE MATCH RULE - ONLY FOR WRITTING$" , "##\t%s = %s"],
+ ]
+ ]
+ ]
+ )
+)
+
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/src/Samba.ycp new/yast2-samba-client-2.13.15/src/Samba.ycp
--- old/yast2-samba-client-2.13.14/src/Samba.ycp 2006-03-27 09:26:24.000000000 +0200
+++ new/yast2-samba-client-2.13.15/src/Samba.ycp 2006-04-13 14:11:31.000000000 +0200
@@ -210,7 +210,7 @@
*/
global define boolean GetWinbindCaching () {
- return SambaConfig::GlobalGetStr("pam_winbind:cached_login", "") == "yes" &&
+ return SambaConfig::WinbindGlobalGetStr("cached_login", "") == "yes" &&
SambaConfig::GlobalGetStr("winbind offline logon", "") == "yes";
}
@@ -219,7 +219,7 @@
*/
global define boolean SetWinbindCaching (boolean enable) {
- SambaConfig::GlobalSetStr("pam_winbind:cached_login", enable ? "yes" : nil);
+ SambaConfig::WinbindGlobalSetStr("cached_login", enable ? "yes" : nil);
SambaConfig::GlobalSetStr("winbind offline logon", enable ? "yes" : nil);
return enable;
}
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/src/SambaAD.pm new/yast2-samba-client-2.13.15/src/SambaAD.pm
--- old/yast2-samba-client-2.13.14/src/SambaAD.pm 2006-03-31 09:24:14.000000000 +0200
+++ new/yast2-samba-client-2.13.15/src/SambaAD.pm 2006-04-13 09:16:21.000000000 +0200
@@ -4,7 +4,7 @@
# Summary: Manage AD issues for samba-client
# Authors: Jiri Suchomel
#
-# $Id: SambaAD.pm 29061 2006-03-16 16:56:50Z jsuchome $
+# $Id: SambaAD.pm 29591 2006-03-31 15:19:19Z jsuchome $
#
package SambaAD;
@@ -251,12 +251,12 @@
"template shell" => $remove ? undef : "/bin/bash",
"template homedir" => $remove ? undef : "/home/%D/%U",
"workgroup" => $remove ? undef : $workgroup,
-# "use kerberos keytab" => $remove ? undef : "Yes",
- "pam_winbind:krb5_auth" => $remove ? undef : "yes",
- "pam_winbind:krb5_ccache_type"
- => $remove ? undef : "FILE",
"winbind refresh tickets" => $remove ? undef : "yes"
});
+ SambaConfig->WinbindGlobalSetMap({
+ "krb5_auth" => $remove ? undef : "yes",
+ "krb5_ccache_type" => $remove ? undef : "FILE"
+ });
if (SambaConfig->GlobalGetTruth ("domain logons", 0)) {
SambaConfig->GlobalSetTruth ("domain logons", 0)
}
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/src/SambaConfig.pm new/yast2-samba-client-2.13.15/src/SambaConfig.pm
--- old/yast2-samba-client-2.13.14/src/SambaConfig.pm 2006-03-15 14:49:02.000000000 +0100
+++ new/yast2-samba-client-2.13.15/src/SambaConfig.pm 2006-04-13 14:31:20.000000000 +0200
@@ -30,6 +30,7 @@
# variables
my %Config; # configuration hash
+my %WinbindConfig; # configuration hash for /etc/security/pam_winbind.conf
###########################################################################
# global (static) variables (constants)
@@ -99,6 +100,9 @@
foreach (keys %Config) {
return 1 if $Config{$_}{_modified};
}
+ foreach (keys %WinbindConfig) {
+ return 1 if $WinbindConfig{$_}{_modified};
+ }
return 0;
}
@@ -107,6 +111,7 @@
sub SetModified {
my ($self) = @_;
$Config{"global"}{_modified} = 1;
+ $WinbindConfig{"global"}{_modified} = 1;
return 1;
}
@@ -117,6 +122,9 @@
foreach (keys %Config) {
delete $Config{$_}{_modified};
}
+ foreach (keys %WinbindConfig) {
+ delete $WinbindConfig{$_}{_modified};
+ }
}
# dump configuration to STDOUT (for debugging)
@@ -136,16 +144,18 @@
print "\n";
}
-# read configuration from file
+# read configuration from files:
+# /etc/samba/smb.conf, /etc/security/pam_winbind.conf
BEGIN{ $TYPEINFO{Read} = ["function", "boolean", "boolean"]; }
sub Read {
my ($self, $forceReRead) = @_;
# coniguraton already readed
- return 1 if not $forceReRead and %Config;
+ return 1 if not $forceReRead and %Config and %WinbindConfig;
# forget previous configuration
%Config = ();
+ %WinbindConfig = ();
# read the complete global section
my $AllAtOnce = SCR->Read(".etc.smb.all");
@@ -176,9 +186,128 @@
}
}
}
+
+ # read the complete global section
+ $AllAtOnce = SCR->Read(".etc.security_winbind.all");
+
+ # convert .ini agent all-at-once map to %WinbindConfig
+ foreach my $section (@{$AllAtOnce->{value}}) {
+ next if $section->{kind} ne "section";
+ my $share = $section->{name};
+
+ # disabled (comment-out) share
+ $WinbindConfig{$share}{_disabled} = 1 if $section->{type};
+ my $comment = $section->{comment};
+ $comment =~ s/^[ \t]*[;#]+[ \t]*//gm if $comment;
+ $comment =~ s/^Share disabled by YaST$//mgi if $comment;
+ $comment =~ s/^\n*// if $comment;
+ $comment =~ s/\n*$// if $comment;
+ $WinbindConfig{$share}{_comment} = $comment if $comment;
+
+ foreach my $line (@{$section->{value}}) {
+ next if $line->{kind} ne "value";
+ next if $line->{type} and not $section->{type}; # commented line
+
+ if (defined $WinbindConfig{$share}{$line->{name}}) {
+ $self->WinbindShareAddStr($share, $line->{name},$line->{value});
+ }
+ else {
+ $self->WinbindShareSetStr($share, $line->{name},$line->{value});
+ }
+ }
+ }
$self->UnsetModified();
y2debug("Readed config: ".Dumper(\%Config));
+ y2debug("Readed config: ".Dumper(\%WinbindConfig));
+
+ return 1;
+}
+
+# write /etc/security/pam_winbind.conf.
+BEGIN{ $TYPEINFO{WriteWinbind} = ["function", "boolean", "boolean"]; }
+sub WriteWinbind {
+ my ($self, $forceWrite) = @_;
+
+ y2debug("modified flag is ".($self->GetModified()?"set":"not set"));
+ return 1 unless $forceWrite or $self->GetModified();
+
+ # first, write the global settings complete
+ if ($forceWrite or $WinbindConfig{global}{_modified}) {
+ foreach my $key (sort keys %{$WinbindConfig{global}}) {
+ next if $key =~ /^_/; # skip internal keys
+ my $val = $WinbindConfig{global}{$key};
+ if (!defined $val) {
+ SCR->Write(".etc.security_winbind.value.global.$key", undef);
+ } else {
+ if (ref ($val) ne "ARRAY") {
+ $val = [ String($val) ];
+ }
+ SCR->Write(".etc.security_winbind.value.global.$key", $val);
+ # ensure option is not commented
+ SCR->Write(".etc.security_winbind.value_type.global.$key", [Integer(0)]);
+ }
+ }
+
+ # ensure global section is not commented
+ SCR->Write(".etc.security_winbind.section_type.global", [Integer(0)]);
+
+ # remove modified flag
+ $WinbindConfig{global}{_modified} = undef;
+ }
+
+ # remove removed shares first
+ foreach my $share (sort grep {!$WinbindConfig{$_}} keys %WinbindConfig) {
+ SCR->Write(".etc.security_winbind.section.$share", undef);
+ };
+ $WinbindConfig{_removed} = undef; # remove modified flag
+
+ # write shares
+ foreach my $share (sort keys %WinbindConfig) {
+ next unless $WinbindConfig{$share}; # skip removed shares
+ next if $share eq "global"; # skip global section
+ next if $share =~ /^_/; # skip internal shares
+ next unless $forceWrite || $WinbindConfig{$share}{_modified};
+
+ # prepare the right type for writing out the value
+ my $commentout = $WinbindConfig{$share}{_disabled} ? 1 : 0;
+
+ # write all the options
+ foreach my $key (sort keys %{$WinbindConfig{$share}}) {
+ next if $key =~ /^_/; # skip our internal options
+ my $val = $WinbindConfig{$share}{$key};
+ if (!defined $val) {
+ SCR->Write(".etc.security_winbind.value.$share.$key", undef);
+ } else {
+ if (ref ($val) ne "ARRAY") {
+ $val = [ String($val) ];
+ }
+ my $ret1 = SCR->Write(".etc.security_winbind.value.$share.$key", $val);
+ my $ret = SCR->Write(".etc.security_winbind.value_type.$share.$key", [ Integer($commentout)]);
+ }
+ };
+
+ # write the type and comment of the section
+ SCR->Write(".etc.security_winbind.section_type.$share", [Integer($commentout)]);
+ my $comment = $WinbindConfig{$share}{_comment} || "";
+ $comment =~ s/\n*$//;
+ $comment =~ s/^\n*//;
+ if ($commentout && $comment !~ /.*Share.*Disabled.*/i) {
+ $comment = ($comment?"$comment\n":"") . "## Share disabled by YaST";
+ }
+ $comment =~ s/^(?![#;])/; /mg if $comment;
+ $comment .= "\n" if $comment;
+ SCR->Write(".etc.security_winbind.section_comment.$share", [String("\n$comment")]);
+
+ # remove modified flag
+ $WinbindConfig{$share}{_modified} = undef;
+ };
+
+ # commit the changes
+ if (!SCR->Write(".etc.security_winbind", undef)) {
+ y2error("Cannot write settings to /etc/samba/smb.conf");
+ return 0;
+ }
return 1;
}
@@ -267,8 +396,8 @@
y2error("Cannot write settings to /etc/samba/smb.conf");
return 0;
}
-
- return 1;
+
+ return $self->WriteWinbind ($forceWrite);
}
# return list of shares
@@ -759,5 +888,133 @@
BEGIN{ $TYPEINFO{GlobalGetModified} = ["function", "boolean"]; }
sub GlobalGetModified { return ShareGetModified(shift, "global", @_); }
+##############################################################################
+####### functions related to /etc/security/pam_winbind.conf
+
+# set share modified
+BEGIN{ $TYPEINFO{WinbindShareSetModified} = ["function", "boolean", "string"]; }
+sub WinbindShareSetModified {
+ my ($self, $share) = @_;
+ if (not defined $share) {
+ y2error("undefned share");
+ return undef;
+ }
+ return 0 if $WinbindConfig{$share}{_modified};
+ $WinbindConfig{$share}{_modified} = 1;
+ return 1;
+}
+
+# get share modified
+BEGIN{ $TYPEINFO{WinbindShareGetModified} = ["function", "boolean", "string"]; }
+sub WinbindShareGetModified {
+ my ($self, $share) = @_;
+ if (not defined $share) {
+ y2error("undefned share");
+ return undef;
+ }
+ return $WinbindConfig{$share}{_modified} ? 1 : 0;
+}
+
+# get share key value from /etc/security/pam_winbind.conf
+BEGIN{ $TYPEINFO{WinbindShareGetStr} = ["function", "string", "string", "string", "string"]; }
+sub WinbindShareGetStr {
+ my ($self, $share, $key, $default) = @_;
+ if (not defined $share) {
+ y2error("undefned share");
+ return undef;
+ }
+ if (not defined $key) {
+ y2error("undefned key");
+ return undef;
+ }
+ $key = lc($key);
+ $key = $Synonyms{$key} if exists $Synonyms{$key};
+ if (defined $WinbindConfig{$share}{$key}) {
+ if (ref $WinbindConfig{$share}{$key} eq "ARRAY") {
+ return $WinbindConfig{$share}{$key}[0];
+ }
+ return $WinbindConfig{$share}{$key};
+ }
+ return $default;
+}
+
+# add share key value: used when some key is used multiple times
+BEGIN{ $TYPEINFO{WinbindShareAddStr} = ["function", "boolean", "string", "string", "string"]; }
+sub WinbindShareAddStr {
+ my ($self, $share, $key, $val) = @_;
+ if (not defined $share) {
+ y2error("undefned share");
+ return undef;
+ }
+ if (not defined $key) {
+ y2error("undefned key");
+ return undef;
+ }
+ $key = lc($key);
+ $key = $Synonyms{$key} if exists $Synonyms{$key};
+
+ my $old = $WinbindConfig{$share}{$key};
+ if (ref ($old) ne "ARRAY") {
+ $WinbindConfig{$share}{$key} = [];
+ if (defined $old) {
+ push @{$WinbindConfig{$share}{$key}}, $old;
+ }
+ }
+ push @{$WinbindConfig{$share}{$key}}, $val;
+ return 1;
+}
+
+# set share key value, return old value
+BEGIN{ $TYPEINFO{WinbindShareSetStr} = ["function", "boolean", "string", "string", "string"]; }
+sub WinbindShareSetStr {
+ my ($self, $share, $key, $val) = @_;
+ if (not defined $share) {
+ y2error("undefned share");
+ return undef;
+ }
+ if (not defined $key) {
+ y2error("undefned key");
+ return undef;
+ }
+ my $modified = 0;
+ $key = lc($key);
+ $key = $Synonyms{$key} if exists $Synonyms{$key};
+ my $old = $WinbindConfig{$share}{$key};
+ if (defined $val) {
+ $modified = 1 unless defined $old and $old eq $val;
+ $WinbindConfig{$share}{$key} = $val;
+ } else {
+ $modified = 1 if defined $old;
+ $WinbindConfig{$share}{$key} = undef;
+ }
+ $self->WinbindShareSetModified($share) if $modified;
+ y2debug("WinbindShareSetStr($share, $key, ".($val||"<undef>").")") if $modified;
+ return $modified;
+}
+
+# set share for /etc/security/pam_winbind.conf.
+BEGIN{ $TYPEINFO{WinbindShareSetMap} = ["function", "boolean", "string", ["map", "string", "string"]]; }
+sub WinbindShareSetMap {
+ my ($self, $share, $map) = @_;
+ if (not defined $share) {
+ y2error("undefned share");
+ return undef;
+ }
+ my $modified = 0;
+ foreach(keys %$map) {
+ $modified |= $self->WinbindShareSetStr($share, $_, $map->{$_});
+ }
+ return $modified;
+}
+
+BEGIN{ $TYPEINFO{WinbindGlobalGetStr} = ["function", "string", "string", "string"]; }
+sub WinbindGlobalGetStr { return WinbindShareGetStr(shift, "global", @_); }
+
+BEGIN{ $TYPEINFO{WinbindGlobalSetStr} = ["function", "boolean", "string", "string"]; }
+sub WinbindGlobalSetStr { return WinbindShareSetStr(shift, "global", @_); }
+
+BEGIN{ $TYPEINFO{WinbindGlobalSetMap} = ["function", "boolean", ["map", "string", "string"]]; }
+sub WinbindGlobalSetMap { return WinbindShareSetMap(shift, "global", @_); }
+
1;
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/src/dialogs.ycp new/yast2-samba-client-2.13.15/src/dialogs.ycp
--- old/yast2-samba-client-2.13.14/src/dialogs.ycp 2006-03-16 17:56:53.000000000 +0100
+++ new/yast2-samba-client-2.13.15/src/dialogs.ycp 2006-04-14 12:49:25.000000000 +0200
@@ -280,7 +280,7 @@
} else if (ret == `browse) {
string new_workgroup = BrowseNeighbours( Stage::cont() );
- if( size (new_workgroup) > 0 ) {
+ if( new_workgroup != nil && new_workgroup != "" ) {
// fill in the new workgroup name
UI::ChangeWidget( `id(`workgroup), `Value, new_workgroup );
check_domain_membership (new_workgroup);
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/yast2-samba-client-2.13.14/src/routines.ycp new/yast2-samba-client-2.13.15/src/routines.ycp
--- old/yast2-samba-client-2.13.14/src/routines.ycp 2006-03-27 09:26:24.000000000 +0200
+++ new/yast2-samba-client-2.13.15/src/routines.ycp 2006-04-13 14:15:17.000000000 +0200
@@ -195,6 +195,7 @@
UI::CloseDialog ();
if( groups == nil ) return nil;
+ groups = filter (string group, groups, ``(size (group) > 0));
// let the user choose a workgroup
UI::OpenDialog( `VBox(
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Remember to have fun...