#! /usr/bin/perl -w

#
# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany.
#

use strict;
use XML::Parser;
use XML::Writer;
use Data::Dumper;
use Getopt::Long;
use Encode;
use File::Temp qw(tempfile);
use Sys::Syslog;
use IPC::Open3;
use Fcntl qw(:DEFAULT);
use URI;
use Time::HiRes qw(gettimeofday tv_interval);

my $time = 0;
my $programStartTime = [gettimeofday];

my $version = "1.0";

my $configFile      = "/etc/suseRegister.conf";

my $GUID_FILE       = "/etc/zmd/deviceid";
my $SECRET_FILE       = "/etc/zmd/secret";
my $SYSCONFIG_CLOCK = "/etc/sysconfig/clock";
my $CA_PATH         = "/etc/ssl/certs";

my $URL             = "https://secure-www.novell.com/center/regsvc/";

my $URLlistParams   = "command=listparams";
my $URLregister     = "command=register";
my $URLlistProducts = "command=listproducts";

my $guid      = undef;
my $locale    = undef;
my $encoding  = "utf-8";
my $lang      = "en-US";

my $interactive = 0;
my $listParams  = 0;
my $xmlout      = 0;
my $dumpfilehack = "";
my $dumpxmlfilehack = "";
my $nozypp      = 0;
my $nooptional  = 0;
my $acceptmand  = 0;
my $forcereg    = 0;
my $nohwdata    = 0;
my $logfile     = undef;
my $browser     = undef;
my $forceZmdConfiguration = 0;
my $noproxy     = 0;
my $help        = 0;
my $debug       = 0;
my $yastcall    = 0;
my $LOGDESCR    = undef;
my %args      = (
                 processor => undef,
                 platform => undef,
                 timezone => undef,
                );
my @comlineProducts     = ();
my @serverKnownProducts = ();
my @installedProducts   = ();
my $products = [];
 
my @extraCurlOption = ();
               
my $hostGUID = undef;

my $zmdConfig = {};
my %availableSources = ();
my $ostarget = "";

my $recursion = 0;
my $redirects = 0;
my $registerManuallyURL = ();
my @registerReadableText = ();
my $registerPrivPol = "";

my $querypool     = "/usr/lib/zmd/query-pool";
my $rug           = "/usr/bin/rug";
my $lsb_release   = "/usr/bin/lsb_release";
my $uname         = "/bin/uname";
my $hwinfo        = "/usr/sbin/hwinfo";
my $zmdInit       = "/etc/init.d/novell-zmd";
my $curl          = "/usr/bin/curl";

#my $xenstoreread  = "/usr/bin/xenstore-read";
#my $xenstorewrite = "/usr/bin/xenstore-write";

my $mono          = "/usr/bin/mono";
my $createGuid    = "/usr/lib/suseRegister/bin/create-guid.exe";

my $initnooptional  = 0;
my $lastResponse    = "";
my $initialDomain   = "";

my $rugzmdInstalled = 0;


sub logPrintExit
{
    my $message = shift || undef;
    my $code    = shift || 42;

    if(exists $args{password})
    {
        $args{password} = "secret";
    }
    if(exists $args{passwd})
    {
        $args{passwd} = "secret";
    }
    if(exists $args{secret})
    {
        $args{secret} = "secret";
    }
    my $cmdtxt = "Commandline params: no-optional:$nooptional  forceregistration:$forcereg  ";
    $cmdtxt .= "no-hw-data:$nohwdata  list-params:$listParams  forcezmdconfig:$forceZmdConfiguration  ";
    $cmdtxt .= "interactive:".((defined $interactive)?"$interactive":"0")."  browser:".((defined $browser)?"$browser":"");

    syslog("err", $cmdtxt);
    syslog("err", "Argument Dump: ".Data::Dumper->Dump([{%args}]));
    syslog("err", "Products Dump: ".Data::Dumper->Dump([$products]));
    syslog("err", "$message($code)");
    print STDERR $message;

    closelog;
    close $LOGDESCR if(defined $LOGDESCR);
    
    print STDERR "total time: ".(tv_interval($programStartTime))."\n" if($time);
    
    exit $code;
}


sub intersection
{
    my $arr1 = shift || undef;
    my $arr2 = shift || undef;
    my $ret = [];
    
    if(!defined $arr1 || !defined $arr2 || 
       ref($arr1->[0]) ne "ARRAY" || ref($arr2->[0]) ne "ARRAY")
    {
        return [];
    }

    print STDERR "intersect1: ".Data::Dumper->Dump([$arr1])."\n" if($debug >= 3);
    print STDERR "intersect2: ".Data::Dumper->Dump([$arr2])."\n" if($debug >= 3);
    
    foreach my $v1 (@$arr1)
    {
        foreach my $v2 (@$arr2) 
        {
            if(lc($v1->[0]) eq lc($v2->[0]))
            {
                if($v2->[1] ne "0")
                {
                    push @$ret, $v2;
                }
                else
                {
                    push @$ret, $v1;
                }
                last;
            }
        }
    }
    
    print STDERR "intersect return : ".Data::Dumper->Dump([$ret])."\n" if($debug >= 3);
    return $ret;
}


sub fullpathOf 
{
    my $program = shift || undef;
    
    if(!defined $program || $program eq "" || $program !~ /^[\w_-]+$/)
    {
        return undef;
    }
    
    my $fullpath = `which $program 2>/dev/null`;
    chomp($fullpath);
    print STDERR "Fullpath:$fullpath\n" if($debug >=2);
    
    if (defined $fullpath && $fullpath ne "")
    {
        return $fullpath;
    }
    return undef;
}



sub readSystemValues
{
    my $t0 = [gettimeofday] if($time);
    
    ############### read the config ###################
    if(-e "$configFile")
    {
        open(CNF, "< $configFile") or do {
            logPrintExit("cannot open file $configFile: $!\n", 12);
        };
        
        while(<CNF>)
        {
            if($_ =~ /^\s*#/)
            {
                next;
            }
            elsif($_ =~ /^url\s*=\s*(\S*)\s*/ && defined $1 && $1 ne "") 
            {
                $URL = $1;
            }
            elsif($_ =~ /^listParams\s*=\s*(\S*)\s*/ && defined $1 && $1 ne "") 
            {
                $URLlistParams = $1;
            }
            elsif($_ =~ /^listProducts\s*=\s*(\S*)\s*/ && defined $1 && $1 ne "") 
            {
                $URLlistProducts = $1;
            }
            elsif($_ =~ /^register\s*=\s*(\S*)\s*/ && defined $1 && $1 ne "")
            {
                $URLregister = $1;
            }
            elsif($_ =~ /^hostGUID\s*=\s*(\w*)\s*/ && defined $1 && $1 ne "")
            {
                $hostGUID = $1;
            }
        }
        close CNF;
    }
    
    ############### GUID ##############################

    if(!-e $GUID_FILE)
    {
        rugStart();
    }
    
    open(ZMD, "< $GUID_FILE") or do {
        logPrintExit("cannot open file $GUID_FILE: $!\n", 12);
    };
    
    $guid = <ZMD>;
    chomp($guid);
    close ZMD;
    
    print STDERR "GUID:$guid\n" if($debug);

    ############### find Products #####################

    rugProducts();

    ########## host GUID (virtualization) #############

# spec not finished and this seems not to work

#     if(-d "/proc/xen" &&
#        -e $xenstoreread) 
#     {
#         print STDERR "Found XEN\n" if($debug >= 2);
        
#         # FIXME: check if this command really returns what we want
#         my $val = `$xenstoreread domid 2>/dev/null`;
#         chomp($val);
        
#         if(defined $val && $val eq "0")
#         {
#             print STDERR "We are Domain-0\n" if($debug >= 2);
            
#             # we are Domain-0    
#             if(-e $xenstorewrite) 
#             {
#                 print STDERR "Write hostGUID to xenbus\n" if($debug);
                
#                 `$xenstorewrite hostGUID $guid 2>/dev/null`;
#                 # FIXME: after rebooting Domain-0 this value might be gone
#             }
#         }
#         else
#         {
#             print STDERR "try to read hostGUID from xenbus\n" if($debug >= 2);
            
#             $val = `$xenstoreread hostGUID 2>/dev/null`;
#             chomp($val);
            
#             if(defined $val && $val ne "") 
#             {
#                 print STDERR "Got hostGUID: $val\n" if($debug);
                
#                 $hostGUID = $val;
#             }
#         }
#     }

    ############## some initial values ########################

    $args{processor} = `$uname -p`;
    $args{platform}  = `$uname -i`;
    $args{timezone}  = "US/Mountain";      # default


    open(SYSC, "< $SYSCONFIG_CLOCK") or do {
        logPrintExit("cannot open file $SYSCONFIG_CLOCK: $!\n", 12);
    };
    while(<SYSC>) 
    {
        if($_ =~ /^TIMEZONE\s*=\s*"?([^"]*)"?/) 
        {
            if(defined $1 && $1 ne "")
            {
                $args{timezone}  = $1;
            }
        }
    }
    close SYSC;
    
    chomp($args{processor});
    chomp($args{platform});

    print STDERR "readSystemValues: ".(tv_interval($t0))."\n" if($time);
}


sub evaluateCommand
{
    my $command   = shift || undef;
    my $mandatory = shift || 0;
    my $cmd       = undef;
    my $out       = undef;
    my @arguments = ();

    if (!defined $command || $command eq "")
    {
        logPrintExit("No command.\n", 14);
    }

    if ($command =~ /^hwinfo\s*(.*)\s*$/)
    {
        if(!$nohwdata)
        {
            $cmd = $hwinfo;
            if (defined $1)
            {
                @arguments = split(/\s+/, $1);
            }
        }
        elsif($mandatory)
        {
            logPrintExit("Mandatory hardware data cannot be supplied because --no-hw-data was given.\n",
                         3);
        }
        else
        {
            return "DISCARDED";
        }
    }
    elsif ($command =~ /^lsb_release\s*(.*)\s*$/)
    {
        # maybe lsb_release is not installed
        if(-e $lsb_release)
        {
            $cmd = $lsb_release;
            if (defined $1) 
            {
                @arguments = split(/\s+/, $1);
            }
        }
        else
        {
            return "";
        }
    }
    elsif ($command =~ /^uname\s*(.*)\s*$/)
    {
        $cmd = $uname;
        if (defined $1)
        {
            @arguments = split(/\s+/, $1);
        }
    }
    elsif ($command =~ /^zmd-secret$/)
    {
        $cmd = undef;
        open(SEC, "< $SECRET_FILE") or do {
            logPrintExit("cannot open file $SECRET_FILE: $!\n", 12);
        };
        while(<SEC>)
        {
            $out .= $_;
        }
        close SEC;
    }
    elsif ($command =~ /^zmd-ostarget$/)
    {
        $cmd = undef;

        rugOSTarget();
        
        $out = $ostarget;
    }
    else
    {
        $out = "DISCARDED"; # command not allowed; reply DISCARDED
        $cmd = undef;
    }

    if (defined $cmd)
    {
        print "Execute $cmd ".join(" ", @arguments)."\n" if($debug);
        
        my $pid = open3(\*IN, \*OUT, \*ERR, $cmd, @arguments) or do {
            logPrintExit("Cannot execute $cmd ".join(" ", @arguments).": $!\n",13);
        };

        while (<OUT>)
        {
            $out .= "$_";
        }
        #chomp($msg) if(defined $msg && $msg ne "");
        while (<ERR>)
        {
            $out .= "$_";
        }
        close OUT;
        close ERR;
        close IN;
        waitpid $pid, 0;
        chomp($out) if(defined $out && $out ne "");
        if(!defined $out || $out eq "") 
        {
            $out = "";
        }
        print STDERR "LENGTH: ".length($out)."\n" if($debug);
    }
    
    return $out;
}

sub evalInteractiveArgs
{
    my $tree      = shift || undef;
    my $logic     = shift || "";
    my $indent    = shift || "";
    my $mandatory = shift || 0;

    my $mandstr = "";

    my $nextLogic = $logic;
    if($#registerReadableText >= 0) 
    {
        $indent = $indent."  ";
    }
        
    if (! defined $tree)
    {
        logPrintExit("No data\n", 14);
    }
 
    print STDERR "LOGIC: $logic\n" if($debug >= 3);
    print STDERR Data::Dumper->Dump([$tree])."\n" if($debug >= 3);

    for (my $i = 0; $i < @{$tree}; $i++)
    {
        next if(!defined $tree->[$i]);
        
        if ($tree->[$i] eq "param") 
        {
            if (@{$tree->[$i+1]} > 1)
            {
                $nextLogic = "AND";
            }
            if($logic eq "") 
            {
                $logic = $nextLogic;
            }
        }
        elsif ($tree->[$i] eq "select")
        {
            $nextLogic = "OR";
            if($logic eq "") 
            {
                $logic = $nextLogic;
            }
        }
        elsif ($tree->[$i] eq "host" || $tree->[$i] eq "guid")
        {
            # skip host and guid. There are no strings for the user to display.
            $i++;
            next;
        }
        elsif ($tree->[$i] eq "privacy")
        { 
            $i++;
            my $hash = $tree->[$i]->[0];
            if (exists $hash->{description} && defined $hash->{description})
            {
                if(!$yastcall)
                {
                    $registerPrivPol .= "\nInformation on Novell's Privacy Policy:\n";
                    $registerPrivPol .= $hash->{description}."\n";
                }
                else
                {
                    $registerPrivPol .= "<p>Information on Novell's Privacy Policy:<br>\n";
                    $registerPrivPol .= $hash->{description}."</p>\n";
                }
            }
            
            if (exists $hash->{url} && defined $hash->{url} && $hash->{url} ne "")
            {
                if(!$yastcall)
                {
                    $registerPrivPol .= $hash->{url}."\n";
                }
                else
                {
                    $registerPrivPol .= "<p><a href=\"".$hash->{url}."\">";
                    $registerPrivPol .= $hash->{url}."</a></p>\n";
                }
            }
        }
        elsif ( ref($tree->[$i]) eq "ARRAY")
        { 
            evalInteractiveArgs($tree->[$i], $nextLogic, $indent, $mandatory);
            $nextLogic = $logic;
            if (defined $registerReadableText[$#registerReadableText] &&
                $registerReadableText[$#registerReadableText] =~ /^\s*AND|OR\s*$/i)
            {
                if ($logic =~ /^\s*$/)
                {
                    pop @registerReadableText;
                }
                else
                {
                    $registerReadableText[$#registerReadableText] = $indent."$logic\n";
                }                
            }
            else
            {
                push @registerReadableText, $indent."$logic\n";
            }
        }
        elsif ( ref($tree->[$i]) eq "HASH")
        {
            if (exists $tree->[$i]->{class} &&
                defined $tree->[$i]->{class} &&
                $tree->[$i]->{class} eq "mandatory")
            {
                $mandatory = 1;
                $mandstr = "(mandatory)";
                print STDERR "Found mandatory\n" if($debug >= 3);
            }
            elsif(!$mandatory &&
                  !exists $tree->[$i]->{class})
            {
                $mandstr = "(optional)";
            }
            
            if (@$tree == 1 &&
                defined $tree->[$i]->{description} && defined $tree->[$i]->{id} )
            {
                if ( ($initnooptional && $mandatory) || !$initnooptional)
                {
                    print STDERR "Add instruction\n" if($debug >= 3);
                    
                    my $txt = $indent."* ".$tree->[$i]->{description}." $mandstr";
                    if(!$yastcall)
                    {
                        $txt .= ":\t".$tree->[$i]->{id}."=<value>\n";
                    }
                    else
                    {
                        $txt .= "\n";
                    }
                    push @registerReadableText, $txt;
                }
            }
            elsif (defined $tree->[$i]->{description})
            {
                if ( ($initnooptional && $mandatory) || !$initnooptional)
                {
                    print STDERR "Add description\n" if($debug >= 3);
                    push @registerReadableText, $indent.$tree->[$i]->{description}." $mandstr with:\n";
                }
            }
        }
    }
}

sub addAutomaticArgs
{
    my $tree      = shift || undef;
    my $mandatory = shift || 0;
    my $modified  = shift || 0;

 
    if (! defined $tree)
    {        
        logPrintExit("No data.\n",14);
    }
 
    for (my $i = 0; $i < @{$tree}; $i++)
    {
        next if(!defined $tree->[$i]);

        if ( ref($tree->[$i]) eq "ARRAY")
        { 
            $modified = addAutomaticArgs($tree->[$i], $mandatory, $modified);
        }
        elsif ( ref($tree->[$i]) eq "HASH")
        {
            if (exists $tree->[$i]->{class} &&
                defined $tree->[$i]->{class} &&
                $tree->[$i]->{class} eq "mandatory")
            {
                $mandatory = 1;
            }
            
            if (@$tree == 1 &&
                defined $tree->[$i]->{command} &&
                defined $tree->[$i]->{id})
            {                
                if( ($nooptional && $mandatory) || !$nooptional)
                {
                    my $ret = evaluateCommand($tree->[$i]->{command}, $mandatory);
                    if (defined $ret)
                    {                        
                        $args{$tree->[$i]->{id}} = $ret;
                        $modified = 1;
                    }
                }
            }
        }
    }
    return $modified;
}

sub walkResultZmdconfig
{
    my $tree        = shift || undef;
    my $serviceName = shift || undef;
    my $paramName   = shift || undef;
    my $catalogURL  = shift || undef;

    if (!defined $tree)
    {
        logPrintExit("No data.\n", 14);
    }

    for (my $i = 0; $i < @$tree; $i++)
    {
        next if(!defined $tree->[$i]);
        
        if ($tree->[$i] eq "service" && 
            ref($tree->[$i+1]) eq "ARRAY" &&
            ref($tree->[$i+1]->[0]) eq "HASH" &&
            exists $tree->[$i+1]->[0]->{id})
        {
            $serviceName = $tree->[$i+1]->[0]->{id};
            if (exists $tree->[$i+1]->[0]->{type})
            {                
                $zmdConfig->{$serviceName}->{type} = $tree->[$i+1]->[0]->{type};
            }
        }
        elsif (ref($tree->[$i]) eq "ARRAY")
        {
            walkResultZmdconfig($tree->[$i], $serviceName, $paramName, $catalogURL);
            
            if (defined $paramName)
            {
                $paramName = undef;
            }
            elsif (defined $serviceName)
            {
                $serviceName = undef;
            }
        }
        elsif ($serviceName &&
               $tree->[$i] eq "param" && 
               ref($tree->[$i+1]) eq "ARRAY" &&
               ref($tree->[$i+1]->[0]) eq "HASH")
        {
            if (exists $tree->[$i+1]->[0]->{id})
            {                
                $paramName = $tree->[$i+1]->[0]->{id};
            }
            elsif (exists $tree->[$i+1]->[0]->{name})
            {                
                $paramName = $tree->[$i+1]->[0]->{name};
                
                if($paramName eq "catalog") 
                {
                    if(exists $tree->[$i+1]->[0]->{url})
                    {
                        $catalogURL = $tree->[$i+1]->[0]->{url};
                    }
                    else
                    {
                        $catalogURL = undef;
                    }
                }
            }
        }
        elsif ($serviceName && 
               $paramName &&
               $tree->[$i] == 0 )
        {
            $i++;
            if ($paramName eq "catalog") # more than one catalog allowed
            {                
                $zmdConfig->{$serviceName}->{catalog}->{$tree->[$i]} = $catalogURL;
            }
            else
            {
                $zmdConfig->{$serviceName}->{$paramName} = $tree->[$i];
            }
            return;
        }
        elsif (!defined $serviceName &&
               $tree->[$i] eq "param" && 
               ref($tree->[$i+1]) eq "ARRAY" &&
               ref($tree->[$i+1]->[0]) eq "HASH")
        {
            if (exists $tree->[$i+1]->[0]->{id})
            {                
                $paramName = $tree->[$i+1]->[0]->{id};
            }
        }
        elsif (! defined $serviceName && 
               $paramName &&
               $tree->[$i] == 0 )
        {
            $i++;
            $zmdConfig->{"globalzmdoptions"}->{$paramName} = $tree->[$i];
        }
    }
}

sub buildXML
{
    my $output = '<?xml version="1.0" encoding="utf-8"?>';
    
    my $writer = new XML::Writer(OUTPUT => \$output);

    my %a = ("xmlns" => "http://www.novell.com/xml/center/regsvc-1_0");
    
    if(!$nooptional)
    {
        $a{accept} = "optional";
    }
    if($acceptmand) 
    {
        $a{accept} = "mandatory";
    }
    if($forcereg) 
    {
        $a{force} = "registration";
    }
    
    $writer->startTag("register", %a);
    
    $writer->startTag("guid");
    $writer->characters($guid);
    $writer->endTag("guid");

    if(defined $hostGUID && $hostGUID ne "") 
    {
        $writer->startTag("host");
        $writer->characters($hostGUID);
        $writer->endTag("host");
    }
    else
    {
        $writer->emptyTag("host");
    }
    
    foreach my $PArray (@$products)
    {
        if(defined $PArray->[0] && $PArray->[0] ne "" &&
           defined $PArray->[1] && $PArray->[1] ne "")
        {
            $writer->startTag("product",
                              "version" => $PArray->[1],
                              "release" => $PArray->[2],
                              "arch"    => $PArray->[3]);
            if ($PArray->[0] =~ /\s+/)
            {
                $writer->cdata($PArray->[0]);
            }
            else
            {
                $writer->characters($PArray->[0]);
            }
            $writer->endTag("product");
        }
    }
    
    foreach my $key (keys %args)
    {
        next if(!defined $args{$key});

        if($args{$key} eq "")
        {
            $writer->emptyTag("param", "id" => $key);
        }
        else
        {
            $writer->startTag("param",
                              "id" => $key);
            if ($args{$key} =~ /\s+/)
            {
                $writer->cdata($args{$key});
            }
            else
            {
                $writer->characters($args{$key});
            }
            $writer->endTag("param");
        }
    }
    
    $writer->endTag("register");

    print STDERR "XML:\n$output\n" if($debug >= 3);
    return $output;
}


sub sendData
{
    my $url  = shift || undef;
    my $data = shift || undef;
    
    my $t0 = [gettimeofday] if($time);
    
    my $curlErr = 0;
    my $res = "";
    my $err = "";
    my %header = ();
    my $code = "";
    my $mess = "";
    
    if (! defined $url)
    {
        logPrintExit("Cannot send data. Missing URL.\n", 14);
    }
    if($url =~ /^-/)
    {
        logPrintExit("Wrong protocol($url).\n", 15);
    }

    my $uri = URI->new($url);
    
    if(!defined $uri->host || $uri->host !~ /$initialDomain$/)
    {
        logPrintExit("Wrong URL($url).\n", 15);
    }
    if(!defined $uri->scheme || $uri->scheme ne "https")
    {
        logPrintExit("Wrong protocol($url).\n", 15);
    }
    $url = $uri->as_string;
        
    if (! defined $data)
    {
        logPrintExit("Cannot send data. Missing data.\n", 14);
    }

    my @cmdArgs = ( "--capath", $CA_PATH);

    my $fh = new File::Temp(TEMPLATE => 'dataXXXXX',
                            SUFFIX   => '.xml',
                            DIR      => '/tmp/');
    print $fh $data;

    push @cmdArgs, "--data", "@".$fh->filename();
    push @cmdArgs, "-i";
    push @cmdArgs, "--max-time", "60";

    foreach my $extraOpt (@extraCurlOption)
    {
        if($extraOpt =~ /^([\w-]+)[\s=]*(.*)/)
        {
            if(defined $1 && $1 ne "")
            {
                push @cmdArgs, $1;
                
                if(defined $2 && $2 ne "")
                {
                    push @cmdArgs, $2;
                }
            }
        }
    }
    
    push @cmdArgs, "$url";

    print STDERR "Call $curl ".join(" ", @cmdArgs)."\n" if($debug >= 2);
    print STDERR "SEND DATA to URI: $url:\n" if($debug >= 2);
    print STDERR "$data\n"  if($debug >= 2);

    print $LOGDESCR "\nSEND DATA to URI: $url:\n" if(defined $LOGDESCR);
    print $LOGDESCR "$data\n"  if(defined $LOGDESCR);

    if($noproxy)
    {
        delete $ENV{'http_proxy'};
        delete $ENV{'HTTP_PROXY'};
        delete $ENV{'https_proxy'};
        delete $ENV{'HTTPS_PROXY'};
        delete $ENV{'ALL_PROXY'};
        delete $ENV{'all_proxy'};
    }
    $ENV{'PATH'} = '/bin:/usr/bin:/sbin:/usr/sbin:/opt/kde3/bin/:/opt/gnome/bin/';

    my $pid = open3(\*IN, \*OUT, \*ERR, $curl, @cmdArgs) or do {
        logPrintExit("Cannot execute $curl ".join(" ", @cmdArgs).": $!\n",13);
    };

    my $foundBody = 0;
    while (<OUT>)
    {
        $res = "" if(! defined $res);
        if ($foundBody)
        {
            $res .= "$_";
        }
        elsif ($_ =~ /^HTTP\/\d\.\d\s(\d+)\s(.*)$/)
        {
            if (defined $1 && $1 ne "")
            {
                $code = $1;
            }
            if (defined $2 && $2 ne "")
            {
                $mess = $2;
            }
        }
        elsif ($_ =~ /^[\w-]+:\s/)
        {
            my ($key, $val) = split(": ", $_, 2);
            $header{$key} = $val;
        }
        elsif ($_ =~ /^\s*</)
        {
            $foundBody = 1;
            $res .= "$_";
        }
    }
    while (<ERR>)
    {
        $err .= "$_";
    }
    close OUT;
    close ERR;
    close IN;
    waitpid $pid, 0;

    $curlErr = ($?>>8);

    print STDERR "CURL RETURN WITH: $curlErr\n" if($debug >= 2);
    print STDERR "\nRECEIVED DATA:\n" if($debug >= 2);
    print STDERR "CODE: $code MESSAGE:  $mess\n" if($debug >= 2);
    print STDERR "HEADER: ".Data::Dumper->Dump([\%header])."\n" if($debug >= 2);
    print STDERR "BODY:  $res\n" if($debug >= 2);
    
    print $LOGDESCR "\nRECEIVED DATA:\n" if(defined $LOGDESCR);
    print $LOGDESCR "CURL RETURN WITH: $curlErr\n" if(defined $LOGDESCR);
    print $LOGDESCR "CODE: $code MESSAGE:  $mess\n" if(defined $LOGDESCR);
    print $LOGDESCR "HEADER: ".Data::Dumper->Dump([\%header])."\n" if(defined $LOGDESCR);
    print $LOGDESCR "BODY:  $res\n" if(defined $LOGDESCR);

    if ($curlErr != 0)
    {
        logPrintExit("Execute curl command failed with '$curlErr': $err", 4);
    }

    if ($code >= 300 && exists $header{Location} && defined $header{Location})
    {
        if ($redirects > 5)
        {
            logPrintExit("Too many redirects\n", 5);
        }
        $redirects++;
        
        my $loc = $header{Location};

        local $/ = "\r\n";
        chomp($loc);
        local $/ = "\n";

        print STDERR "sendData(redirect): ".(tv_interval($t0))."\n" if($time);

        $res = sendData($loc, $data);
    }
    elsif($code < 200 || $code >= 300) 
    {
        my $b = "";
        my @c = ();

        if(-e "/usr/bin/lynx")
        {
            $b = "/usr/bin/lynx";
            push @c, "-dump", "-stdin";
        }
        elsif(-e "/usr/bin/w3m") 
        {
            $b = "/usr/bin/w3m";
            push @c, "-dump", "-T", "text/html";
        }
                
        open(LYNXIN, "|-", "$b", @c) or do {
            logPrintExit("Cannot execute $b: $!\n$res\n", 13);
        };
        
        print LYNXIN $res;
        close LYNXIN;
        logPrintExit("ERROR: $code: $mess\n", 2);
    }
    else
    {
        print STDERR "sendData(final): ".(tv_interval($t0))."\n" if($time);
    }
    
    return $res;
}

sub listParams
{
    my $output = '<?xml version="1.0" encoding="utf-8"?>';
    
    my $writer = new XML::Writer(OUTPUT => \$output);

    $writer->startTag("listparams", "xmlns" => "http://www.novell.com/xml/center/regsvc-1_0");

    foreach my $PArray (@$products)
    {
        if(defined $PArray->[0] && $PArray->[0] ne "" &&
           defined $PArray->[1] && $PArray->[1] ne "")
        {
            $writer->startTag("product",
                              "version" => $PArray->[1],
                              "release" => $PArray->[2],
                              "arch"    => $PArray->[3]);
            if ($PArray->[0] =~ /\s+/)
            {
                $writer->cdata($PArray->[0]);
            }
            else
            {
                $writer->characters($PArray->[0]);
            }
            $writer->endTag("product");
        }
    }
    
    $writer->endTag("listparams");

    print STDERR "XML:\n$output\n" if($debug >= 3);

    $redirects = 0;
    my $res = sendData($URL."?".$URLlistParams."&lang=$lang&version=$version", $output);
    
    if ($xmlout)
    {
        print "$res\n";
    }
    else
    {
        my $text = "";
        my $privpol = "";
        
        if(!$yastcall)
        {
            $text .= "Available Options:\n\n";
            $text .= "You can add these options to suse_register with the -a option.\n";
            $text .= "-a can be used multiple times\n\n";
        }
        
        my $p = new XML::Parser(Style => 'Tree');
        my $tree = $p->parse($res);

        #print Data::Dumper->Dump([$tree])."\n";

        if (! defined $tree || $tree->[0] ne "paramlist" || ref($tree->[1]) ne "ARRAY")
        {
            logPrintExit("Unknown format. Cannot show human readable output. Try --xml-output.\n".
                         6);
        }

        if($yastcall)
        {
            $text .= "<pre>";
        }
                
        for (my $i = 0; $i < @{$tree->[1]}; $i++)
        {
            #print Data::Dumper->Dump([$tree->[1]->[$i]])."\n\n";

            if (defined $tree->[1]->[$i]    && 
                $tree->[1]->[$i] eq "param" &&
                ref($tree->[1]->[$i+1]) eq "ARRAY" &&
                ref($tree->[1]->[$i+1]->[0]) eq "HASH" )
            {
                $i++;
                my $hash = $tree->[1]->[$i]->[0];

                if (exists $hash->{command} && defined $hash->{command} &&
                    $nohwdata && $hash->{command} =~ /^hwinfo/)
                {
                    # skip; --no-hw-data was provided 
                    next;
                }
                
                $text .= "* ".$hash->{description}.": ";
                if(!$yastcall)
                {
                    $text .= "\n\t".$hash->{id}."=<value> ";
                }

                if (exists $hash->{command} && defined $hash->{command} && $hash->{command} ne "")
                {
                    $text .= "(command: ".$hash->{command}.")\n";
                }
                else
                {
                    $text .= "\n";
                }
                if(!$yastcall)
                {
                    $text .= "\n";
                }
            }
            elsif (defined $tree->[1]->[$i]    && 
                   $tree->[1]->[$i] eq "privacy" &&
                   ref($tree->[1]->[$i+1]) eq "ARRAY" &&
                   ref($tree->[1]->[$i+1]->[0]) eq "HASH" )
            {
                $i++;
                my $hash = $tree->[1]->[$i]->[0];

                if (exists $hash->{description} && defined $hash->{description})
                {
                    if(!$yastcall) 
                    {
                        $privpol .= "\nInformation on Novell's Privacy Policy:\n";
                        $privpol .= $hash->{description}."\n";
                    }
                    else
                    {
                        $privpol .= "<p>Information on Novell's Privacy Policy:<br>\n";
                        $privpol .= $hash->{description}."</p>\n";
                    }
                }
                
                if (exists $hash->{url} && defined $hash->{url} && $hash->{url} ne "")
                {
                    if(!$yastcall) 
                    {
                        $privpol .= $hash->{url}."\n";
                    }
                    else
                    {
                        $privpol .= "<p><a href=\"".$hash->{url}."\">";
                        $privpol .= $hash->{url}."</a></p>\n";
                    }
                }
            }
        }
        if(!$yastcall)
        {
            $text .= "Example:\n";
            $text .= "  suse_register -a username=\"tux\"\n";
        }
        else
        {
            $text .= "</pre>\n";
        }

        $text .= $privpol;

        print "$text\n";
    }
    return 0;
}

sub rugStart
{
    my $msg = "";
    my $code = 1;

    my $t0 = [gettimeofday] if($time);

    if($rugzmdInstalled)
    {
        $msg = `$zmdInit status 2>&1`;
        $code = ($?>>8);
        
        if($code != 0) 
        {
            $msg = `$zmdInit start 2>&1`;
            $code = ($?>>8);
            
            sleep 2;
            
            foreach my $cnt (1..10)
            {
                if(-e $GUID_FILE)
                {
                    last;
                }
                sleep 1;
            }
            
            my $msg2 = `$zmdInit status 2>&1`;
            $code = ($?>>8);
            
            if($code != 0) 
            {
                logPrintExit("Cannot start zmd: ".($msg?$msg:"").($msg2?$msg2."($code)":"($code)"), 7);
            }
        }
        print "rugStart: ".(tv_interval($t0))."\n" if($time);
        return $code;
    }
    else
    {
        if(!-e $GUID_FILE)
        {
            if(!-d "/etc/zmd")
            {
                mkdir "/etc/zmd" or logPrintExit("cannot create directory /etc/zmd: $!\n", 12);
            }
            
            my $guid = `$mono $createGuid 2>/dev/null`;
            if(!defined $guid || $guid eq "")
            {
                logPrintExit("Cannot create guid", 13);
            }
            open(ZMD, "> $GUID_FILE") or do {
                logPrintExit("cannot open file $GUID_FILE for write: $!\n", 12);
            };
            print ZMD $guid;
            close ZMD;
            print STDERR "GUID created: $guid\n" if($debug);
        }
    }
    print "rugStart: ".(tv_interval($t0))."\n" if($time);
    return 0;
}

sub rugSearchServices
{
    my $msg = "";
    my $code = 1;

    $code = rugStart();

    if(!$rugzmdInstalled)
    {
        # If rug/zmd are not installed we are done here.
        syslog("info", "rug/zmd are not installed. finish.");
        return 0;
    }

    $msg = `$rug --terse --no-abbrev sl`;
    $code = ($?>>8);

    if ($msg =~ /^---\sNo services found\s---/)
    {
        return 0;
    }
    
    foreach my $line (split("\n", $msg)) 
    {
        my($num, $stat, $type, $name, $uri) = split('\s*\|\s*', $line, 5);
        print STDERR "Found $num, $stat, $type, $name, $uri\n" if($debug >= 2 && defined $num && defined $stat);
        
        if(defined $name && $name ne "" &&
           defined $num && $num ne "") 
        {
            if(defined $type && lc($type) eq "rce")
            {
                $availableSources{$num} = $uri;
            }
            else
            {
                $availableSources{$num} = $name;
            }
        }
    }
    
    return 0;
}

sub rugOSTarget
{
    my $msg = "";
    my $code = 1;

    $code = rugStart();

    if(!$rugzmdInstalled)
    {
        # If rug/zmd are not installed we are done here.
        syslog("info", "rug/zmd are not installed. finish.");
        return 0;
    }

    $msg = `$rug --terse ping`;
    $code = ($?>>8);

    foreach my $line (split("\n", $msg)) 
    {
        if($line =~ /^OS\s+Target:\s*(.+)/)
        {
            if(defined $1 && $1 ne "")
            {
                $ostarget = $1;
            }
        }
    }
    
    return $code;
}

sub rugProducts
{
    my $t0 = [gettimeofday] if($time);
    
    print STDERR "query pool command: $querypool products \@system \n" if($debug >= 1);
    
    my $result = `$querypool products \@system`;
    my $code = ($?>>8);

    if($code != 0) 
    {
        $code += 30;
        syslog("err", "Query products failed($code): $result");
    }
    else 
    {
        foreach my $line (split("\n", $result))
        {
            next if($line =~ /^\s*$/);
            
            my @p = split('\|', $line);
            my $installed = $p[0];
            my $type      = $p[1];
            my $product   = $p[2];
            my $version   = $p[3];
            my $release   = "";
            my $arch      = $p[4];

            if(!defined $arch || $arch eq "" || $arch eq "noarch")
            {
                $arch = `$uname -i`;
                chomp($arch);
            }
            
            if(defined $version && $version ne "")
            {
                my @v = split("-", $version, 2);
                if(exists $v[0] && defined $v[0] && $v[0] ne "")
                {
                    $version = $v[0];
                }
                if(exists $v[1] && defined $v[1] && $v[1] ne "")
                {
                    $release = $v[1];
                }
            }
            
            if($installed eq "i" && lc($type) eq lc("product"))
            {
                push @installedProducts, [$product, $version, $release, $arch];
            }
        }
    }
    
    print STDERR "Query products failed($code): $result\n" if($debug && $code != 0);
    
    print STDERR "installed products:           ".Data::Dumper->Dump([\@installedProducts])."\n" if($debug);
    syslog("info", "Installed Products Dump: ".Data::Dumper->Dump([\@installedProducts]));

    print STDERR "rugProducts: ".(tv_interval($t0))."\n" if($time);
    
}

sub rugServiceDelete
{
    my $id  = shift || undef;
    my $msg = "";
    my $t0 = [gettimeofday] if($time);
    
    if(!defined $id)
    {
        return 1;
    }
    
    my @rugArgs = ("--terse", "--no-abbrev", "sd", "$id");

    print STDERR "rug service delete command: $rug ".join(" ",@rugArgs)."\n" if($debug >= 1);
            
    my $pid = open3(\*IN, \*OUT, \*ERR, $rug, @rugArgs) or do {
        logPrintExit("Cannot execute $rug ".join(" ", @rugArgs).": $!\n",13);
    };

    while (<OUT>)
    {
        $msg .= "$_";
    }
    #chomp($msg) if(defined $msg && $msg ne "");
    while (<ERR>)
    {
        $msg .= "$_";
    }
    close OUT;
    close ERR;
    close IN;
    waitpid $pid, 0;
    
    my $code = ($?>>8);
    if($code != 0)
    {
        $code += 20;
    }

    print STDERR "Delete service failed($code): $msg\n" if($debug && $code != 0);
    print STDERR "rugServiceDelete: ".(tv_interval($t0))."\n" if($time);

    return $code;
}


sub rugServiceAdd
{
    my $url     = shift || undef;
    my $type    = shift || undef;
    my $id      = shift || undef;
    my $regcode = shift || undef;
    
    my $msg = "";
    my $t0 = [gettimeofday] if($time);

    if(!$rugzmdInstalled)
    {
        # If rug/zmd are not installed we are done here.
        syslog("info", "rug/zmd are not installed. finish.");
        return 0;
    }

    if (! defined $url || $url eq "") 
    {
        logPrintExit("Missing URL.\n", 14);
    }
    if (! defined $type || $type eq "") 
    {
        $type = "zenworks";
    }

    my @rugArgs = ("--quiet", "sa", "--ignore-failure", "-t", "$type");

    if(defined $regcode && $regcode ne "")
    {
        push @rugArgs, "-k", "$regcode";
    }
    push @rugArgs, "$url";
    

    if(defined $id)
    {
        foreach my $num (keys %availableSources)
        {
            if(defined $availableSources{$num} &&
               $availableSources{$num} eq $id)
            {
                # delete this service
                
                rugServiceDelete($id);
            }
        }
        push @rugArgs, $id;
    }
    
    print STDERR "rug add service command: $rug ".join(" ",@rugArgs)."\n" if($debug >= 1);
            
    my $pid = open3(\*IN, \*OUT, \*ERR, $rug, @rugArgs) or do {
        logPrintExit("Cannot execute $rug ".join(" ", @rugArgs).": $!\n",13);
    };

    while (<OUT>)
    {
        $msg .= "$_";
    }
    #chomp($msg) if(defined $msg && $msg ne "");
    while (<ERR>)
    {
        $msg .= "$_";
    }
    close OUT;
    close ERR;
    close IN;
    waitpid $pid, 0;
    
    my $code = ($?>>8);
    if($code != 0)
    {
        $code += 20;
    }

    print STDERR "Add service failed($code): $msg\n" if($debug && $code != 0);
    print STDERR "rugServiceAdd: ".(tv_interval($t0))."\n" if($time);

    # return $code, but maybe it is ignored
    return ($code,$msg); 
}

sub rugRegister 
{
    my $url     = shift || undef;
    my $regcode = shift || undef;
    my $msg = "";

    if(!$rugzmdInstalled)
    {
        # If rug/zmd are not installed we are done here.
        syslog("info", "rug/zmd are not installed. finish.");
        return 0;
    }

    if (! defined $url || $url eq "") 
    {
        logPrintExit("Missing URL.\n", 14);
    }
    if (! defined $regcode || $regcode eq "") 
    {
        #logPrintExit("Missing regcode.\n", 14);

        # no regcode; no registration required
        return 0;
    }

    my @rugArgs = ("--quiet", "register", "$url", "$regcode");
    
    print STDERR "rug register command: $rug ".join(" ",@rugArgs)."\n" if($debug >= 1);

    my $pid = open3(\*IN, \*OUT, \*ERR, $rug, @rugArgs) or do {
        logPrintExit("Cannot execute $rug ".join(" ", @rugArgs).": $!\n", 13);
    };

    while (<OUT>)
    {
        $msg .= "$_";
    }
    while (<ERR>)
    {
        $msg .= "$_";
    }
    waitpid $pid, 0;
    #chomp($msg) if(defined $msg && $msg ne "");
    close IN;
    close OUT;
    close ERR;

    my $code = ($?>>8);
    if($code != 0)
    {
        $code += 20;
    }
    
    print STDERR "Register failed($code): $msg\n" if($debug && $code != 0);
    return ($code,$msg);
}

sub rugSubscribe 
{
    my $catalogs = shift || undef;
    my $msg  = "";
    my $code = 0;

    if(!$rugzmdInstalled)
    {
        # If rug/zmd are not installed we are done here.
        syslog("info", "rug/zmd are not installed. finish.");
        return 0;
    }

    if (!defined $catalogs || ref($catalogs) ne "HASH") 
    {
        print STDERR "No catalogs to subscribe\n";
        return 1;
    }

    foreach my $catalog (keys %$catalogs) 
    {
        my @rugArgs = ("--terse", "sub", "$catalog");
        
        print STDERR "rug subscribe command: $rug ".join(" ",@rugArgs)."\n" if($debug >= 1);
        
        my $pid = open3(\*IN, \*OUT, \*ERR, $rug, @rugArgs) or do {
            logPrintExit("Cannot execute $rug ".join(" ", @rugArgs).": $!\n", 13);
        };
        
        while (<OUT>)
        {
            $msg .= "$_";
        }
        while (<ERR>)
        {
            $msg .= "$_";
        }
        close IN;
        close OUT;
        close ERR;
        waitpid $pid, 0;

        my $c = ($?>>8);
        if ($c != 0) {
            $code = $c;
        }
    }
    if($code != 0)
    {
        $code += 20;
    }
    
    $msg =~ s/^\s*//;
    $msg =~ s/\s*$//;
    
    print STDERR "Errors during subscibe($code): $msg\n" if($debug && $code != 0);
        
    return ($code,$msg);
}

sub rugPreferences
{
    my $pref  = shift || undef;
    my $value = shift || undef;
    my $msg   = "";
    my $code  = 0;
    
    if(!$rugzmdInstalled)
    {
        # If rug/zmd are not installed we are done here.
        syslog("info", "rug/zmd are not installed. finish.");
        return 0;
    }

    if (!defined $pref || $pref eq "") 
    {
        print STDERR "No preference.\n";
        return 1;
    }
    if (!defined $value || $value eq "") 
    {
        print STDERR "No value for preference $pref.\n";
        return 1;
    }
    
    my @rugArgs = ("--quiet", "set", "$pref", "$value");
            
    print STDERR "rug set-pref command: $rug ".join(" ",@rugArgs)."\n" if($debug >= 1);

    my $pid = open3(\*IN, \*OUT, \*ERR, $rug, @rugArgs) or do {
        logPrintExit("Cannot execute $rug ".join(" ", @rugArgs).": $!\n", 13);
    };
    
    while (<OUT>)
    {
        $msg .= "$_";
    }
    while (<ERR>)
    {
        $msg .= "$_";
    }
    close IN;
    close OUT;
    close ERR;
    waitpid $pid, 0;
    
    $code = ($?>>8);
    if($code != 0)
    {
        $code += 20;
    }

    $msg =~ s/^\s*//;
    $msg =~ s/\s*$//;
    
    print STDERR "Error during set preferences($code): $msg\n" if($debug && $code != 0);
    
    return ($code,$msg);
}

sub configureZMD 
{
    my $tree = shift || undef;
    my $t0 = [gettimeofday] if($time);
    
    if (!defined $tree) 
    {
        logPrintExit("No data.\n", 14);
    }
    
    #print Data::Dumper->Dump([$tree]);

    walkResultZmdconfig($tree);
    
    print Data::Dumper->Dump([$zmdConfig]) if($debug >= 2);


    # HACK: write urls for yast
    if(defined $dumpfilehack && $dumpfilehack ne "")
    {
        my $exc = sysopen(XMLD, $dumpfilehack, O_CREAT|O_TRUNC|O_WRONLY, 0600) and do
        {
            foreach my $service (keys %$zmdConfig) 
            {
                next if($service eq "globalzmdoptions");

                if($zmdConfig->{$service}->{type} eq "yum" || 
                   $zmdConfig->{$service}->{type} eq "zypp")
                {
                    print XMLD $zmdConfig->{$service}->{url}."\n";
                    syslog("info", "dumpfile write(zypp/yum):".$zmdConfig->{$service}->{url});
                }
                elsif($zmdConfig->{$service}->{type} eq "nu" ||
                      $zmdConfig->{$service}->{type} eq "rce")
                {
                    my $secret = "";
                    open(SEC, "< $SECRET_FILE") or do {
                        logPrintExit("cannot open file $SECRET_FILE: $!\n", 12);
                    };
                    while(<SEC>)
                    {
                        $secret .= $_;
                    }
                    close SEC;
                    
                    foreach my $u (keys %{$zmdConfig->{$service}->{catalog}})
                    {
                        my $curi = URI->new($zmdConfig->{$service}->{catalog}->{$u});
                        print $curi->as_string."\n";
                        $curi->userinfo("$guid:$secret");
                        $curi->query_form(auth => "digest", alias => $u);
                        
                        print XMLD $curi->as_string."\n";
                        syslog("info", "dumpfile write(nu):".$u);
                    }
                }
            }
            close XMLD;
        };
        
        if(!defined $exc || $exc == 0)
        {
            syslog("err", "Cannot write to $dumpfilehack: $!");
        }
    }

    if(!$rugzmdInstalled)
    {
        # If rug/zmd are not installed we are done here.
        syslog("info", "rug/zmd are not installed. finish.");

        print STDERR "configureZMD(no zmd): ".(tv_interval($t0))."\n" if($time);
        return 0;
    }
    
    rugSearchServices();
    
    foreach my $service (keys %$zmdConfig) 
    {
        my ($ret, $msg) = (0, "");
        my $name = undef;
        my $catalogs = {};

        next if($service eq "globalzmdoptions");
        
        if($zmdConfig->{$service}->{type} eq "yum" ||
           $zmdConfig->{$service}->{type} eq "zypp")
        {
            # do not add sources of type zypp or yum if parameter --nozypp
            # is given
            next if($nozypp);
            
            if(exists $zmdConfig->{$service}->{catalog} &&
               ref($zmdConfig->{$service}->{catalog}) eq "HASH")

            {
                my @x = keys %{$zmdConfig->{$service}->{catalog}};
                
                if(exists $x[0] && defined $x[0] && $x[0] ne "")
                {
                    $name = $x[0];
                }
            }
            else 
            {
                $name = $service;
            }
            
            $catalogs->{$name} = undef;
        }
        elsif($zmdConfig->{$service}->{type} eq "zenworks" ||
              $zmdConfig->{$service}->{type} eq "nu" ||
              $zmdConfig->{$service}->{type} eq "rce")
        {
            $name = $zmdConfig->{$service}->{url};
            $catalogs = $zmdConfig->{$service}->{catalog};
        }
        
        ($ret, $msg) = rugServiceAdd($zmdConfig->{$service}->{url},
                                     $zmdConfig->{$service}->{type},
                                     $name, $zmdConfig->{$service}->{regcode});
        
        if ($ret != 0)
        {                
            logPrintExit($msg, $ret);
        }

##############################################################
### we register now in rugServiceAdd
##############################################################            
#         if($zmdConfig->{$service}->{type} eq "zenworks" ||
#            $zmdConfig->{$service}->{type} eq "nu")
#         {
#             ($ret, $msg) = rugRegister($zmdConfig->{$service}->{url},
#                                        $zmdConfig->{$service}->{regcode});
#
#             if ($ret != 0) 
#             {                
#                 logPrintExit($msg, $ret);
#             }
#         }
            
        ($ret, $msg) = rugSubscribe($catalogs);
        
        if ($ret != 0) 
        {                
            logPrintExit($msg, $ret);
        }
        if(exists  $zmdConfig->{globalzmdoptions}->{update_inventory} &&
           defined $zmdConfig->{globalzmdoptions}->{update_inventory})
        {
            
            ($ret, $msg) = rugPreferences("inventory-enabled",
                                          $zmdConfig->{globalzmdoptions}->{update_inventory});
            
            if ($ret != 0) 
            {
                logPrintExit($msg, $ret);
            }
        }
    }
    print STDERR "configureZMD: ".(tv_interval($t0))."\n" if($time);
    return 0;
}

sub register 
{
    my $t0 = [gettimeofday] if($time);
    
    my $output = buildXML();

    $redirects = 0;
    my $res = sendData($URL."?".$URLregister."&lang=$lang&version=$version", $output);

    if ($recursion > 5) 
    {
        logPrintExit("Loop detected. Aborting.\n", 8);
    }

    if($res eq $lastResponse)
    {
        logPrintExit("Got the same server response as the last time. Aborting.\n", 9);
    }
    $lastResponse = $res;

    my $p = new XML::Parser(Style => 'Tree');
    my $tree = $p->parse($res);
    
    #print Data::Dumper->Dump([$tree])."\n";

    if (defined $tree && $tree->[0] eq "needinfo" && ref($tree->[1]) eq "ARRAY") 
    {
        my $ret = addAutomaticArgs($tree);

        # addAutomaticArgs has added some args - call register again
        if ($ret == 1)
        {
            $recursion++;
            $acceptmand = 1;
            
            print STDERR "register(auto needinfo): ".(tv_interval($t0))."\n" if($time);
            
            return register();
        }
        else          # no automatic args. Search for interactive args
        {
            if ($xmlout) 
            {
                print "$res\n";

                print STDERR "register(xmlout): ".(tv_interval($t0))."\n" if($time);

                return 1;
            }
            elsif (!$interactive)
            {            
                if (exists $tree->[1]->[0]->{href} && 
                    defined $tree->[1]->[0]->{href} &&
                    $tree->[1]->[0]->{href} ne "")
                {                    
                    $registerManuallyURL = $tree->[1]->[0]->{href};
                    print $registerManuallyURL."\n\n";
                }

                evalInteractiveArgs($tree);
                if ($registerReadableText[$#registerReadableText] =~ /^\s*$/) 
                {
                    pop @registerReadableText;
                }
                if (@registerReadableText == 1) 
                {
                    logPrintExit("Soemthing goes wrong.\n", 10);
                }
            
                if(!$yastcall)
                {
                    unshift @registerReadableText,
                    "To complete the registration please provide some additional parameters:\n\n";
                    
                    push @registerReadableText, "\nYou can provide these parameters with the '-a' option.\n";
                    push @registerReadableText, "You can use the '-a' option multiple times.\n\n";
                    push @registerReadableText, "Example:\n\n";
                    push @registerReadableText, 'suse_register -a email="me@example.com"'."\n";
                    push @registerReadableText, "\nTo register your product manually, use the following URL:\n\n";
                    push @registerReadableText, "$registerManuallyURL\n\n";

                }
                else 
                {
                    unshift @registerReadableText, "<pre>";
                    push @registerReadableText, "</pre>";
                    push @registerReadableText, "<p>To register your product manually, use the following URL:</p>\n";
                    push @registerReadableText, "<pre>$registerManuallyURL</pre>\n\n";
                }
                
                push @registerReadableText, $registerPrivPol;

                print STDERR join("", @registerReadableText)."\n";
                
                print STDERR "register(manual needinfo): ".(tv_interval($t0))."\n" if($time);
                return 1;
            }
            else 
            {
                # INTERACTIVE:
                # search for the URL and open a browser

                my @url = ();
                
                if (exists $tree->[1]->[0]->{href} && 
                    defined $tree->[1]->[0]->{href} &&
                    $tree->[1]->[0]->{href} ne "") 
                {
                    push @url, $tree->[1]->[0]->{href};
                }

                if (@url == 0) 
                {
                    logPrintExit("Missing URL.\n", 14);
                }

                print STDERR "Call URL: ".$tree->[1]->[0]->{href}."\n" if($debug);
                
                if (defined $browser) 
                {
                    if($browser eq "default")
                    {
                        print STDERR "search for default browser\n" if($debug >=2);
                        # some magic to find the default browser
                        if(exists $ENV{DESKTOP_LAUNCH} && 
                           defined $ENV{DESKTOP_LAUNCH}) 
                        {
                            $browser = fullpathOf($ENV{DESKTOP_LAUNCH});
                        }
                        else
                        {
                            if(exists $ENV{DISPLAY}  &&
                               defined $ENV{DISPLAY} &&
                               $ENV{DISPLAY} ne "" )
                            {
                                # GUI Browser

                                $browser = fullpathOf("firefox");
                                
                                if(!defined $browser) 
                                {
                                    $browser = fullpathOf("konqueror");
                                }                                
                            }
                            else
                            {
                                $browser = fullpathOf("lynx");
                                
                                if(!defined $browser) 
                                {
                                    $browser = fullpathOf("w3m");
                                }
                            }
                        }
                    }
                    print STDERR "Want to call browser: $browser\n" if($debug);

                    if(defined $browser && -e $browser)
                    {
                        system($browser, @url);
                        if( ($?>>8) != 0) 
                        {
                            print STDERR "Starting browser '$browser' failed. Disable interactive mode\n";
                            $interactive = 0;
                            $lastResponse = "";
                        }
                        else 
                        {
                            print STDERR "register(browser): ".(tv_interval($t0))."\n" if($time);

                            logPrintExit("If you have finished your registration, ".
                                         "please start suse_register\n".
                                         "again to get the current configuration for ".
                                         "the update mechanism.\n", 0);
                        }
                    }
                    else 
                    {
                        print STDERR "Browser not found. Disable interactive mode\n";
                        $interactive  = 0;
                        $lastResponse = "";
                    }
                }
                else
                {
                    my @defBrowser = ("lynx", "w3m");
                    my $foundBrowser = 0;
                    
                    foreach my $b (@defBrowser) 
                    {
                        my $fullpath = fullpathOf($b);
                        if (defined $fullpath && $fullpath ne "") 
                        {
                            system($fullpath, @url);
                            if( ($?>>8) == 0) 
                            {
                                $foundBrowser = 1;
                                last;
                            }
                        }
                    }
                    if(!$foundBrowser) 
                    {
                        print STDERR "Starting browser failed. Disable interactive mode\n";
                        $interactive = 0;
                        $lastResponse = "";
                    }
                }
            }
            # FIXME: other logic?
            #
            # do not ask for interactive optional again, 
            # because then optional become mandatory
            $nooptional = 1;   

            $recursion++;
            print STDERR "register(browser needinfo): ".(tv_interval($t0))."\n" if($time);

            register();
        }
    }
    elsif (defined $tree && $tree->[0] eq "zmdconfig" && ref($tree->[1]) eq "ARRAY")
    {
        # HACK: write <zmdconfig> xml for yast
        if(defined $dumpxmlfilehack && $dumpxmlfilehack ne "")
        {
            my $exc = open(XMLD, "> $dumpxmlfilehack") and do 
            {
                print XMLD $res."\n";
                syslog("info", "dumpxmlfile writing:$res");
                close XMLD;
            };
            
            if(!defined $exc || $exc == 0)
            {
                syslog("err", "Cannot write to $dumpxmlfilehack: $!");
            }
        }
        
        # ok => configure zmd
        configureZMD($tree->[1]);
    }
    else
    {
        logPrintExit("Unknown reponse format.\n", 11);
    }
    print STDERR "register(zmdconfig): ".(tv_interval($t0))."\n" if($time);
    return 0;
}

sub listProducts
{
    my $t0 = [gettimeofday] if($time);
    
    my $output = "\n";
    
    my $writer = new XML::Writer(OUTPUT => \$output);

    $redirects = 0;

    my $res = sendData($URL."?".$URLlistProducts."&lang=$lang&version=$version", $output);
    
    if ($xmlout)
    {
        print "$res\n";
    }
    else
    {
        my $p = new XML::Parser(Style => 'Tree');
        my $tree = $p->parse($res);
        
        #print Data::Dumper->Dump([$tree])."\n";
        
        if (! defined $tree || $tree->[0] ne "productlist" || ref($tree->[1]) ne "ARRAY")
        {
            logPrintExit("Unknown format. Cannot show human readable output. Try --xml-output.\n".
                         6);
        }

        for (my $i = 0; $i < @{$tree->[1]}; $i++)
        {
            #print Data::Dumper->Dump([$tree->[1]->[$i]])."\n\n";

            if (defined $tree->[1]->[$i]    && 
                $tree->[1]->[$i] eq "product" &&
                ref($tree->[1]->[$i+1]) eq "ARRAY" )
            {
                $i++;
                #print "SEE:".Data::Dumper->Dump([$tree->[1]->[$i]])."\n\n";

                if($tree->[1]->[$i]->[1] == 0    &&
                   defined $tree->[1]->[$i]->[2] &&
                   $tree->[1]->[$i]->[2] ne "")
                {
                    push @serverKnownProducts, [$tree->[1]->[$i]->[2], "0"];
                }
            }
        }
    }
    print STDERR "Server Known Products:".Data::Dumper->Dump([\@serverKnownProducts])."\n" if($debug >= 2);
    print STDERR "listProducts: ".(tv_interval($t0))."\n" if($time);
    return 0;
}

sub usage 
{
    print STDERR "usage: suse_register.pl [-i [-b <path>]] [-n] [--xml-output] [-a <key>=<value> -a ...] [-L <file>] \n";
    print STDERR "       suse_register.pl -p [--xml-output] [--locale=<locale>] [-L <file>]\n";
    print STDERR "       suse_register.pl -h\n\n";
    print STDERR "Options:\n";
    print STDERR "         -i [--interactive]        enable interactive mode\n";
    print STDERR "             --product <product>   product to register\n";
    print STDERR "                                   You can use this option multiple times.\n";
    print STDERR "         -n [--no-optional]        do not send optional data\n";
    print STDERR "         -f [--force-registration] mark all parameters mandatory which are required\n";
    print STDERR "                                   for registration even though registration itself\n";
    print STDERR "                                   might be optional\n";
    print STDERR "             --no-hw-data          do not send hardware data, even if they are mandatory\n";
#    print STDERR "            [--forceZmdConfiguration] delete current zmd configuration and apply new values\n";
    print STDERR "      -h -? [--help]               show this help\n";
    print STDERR "         -a [--arg] <key>=<value>  provide an additional argument 'key'\n";
    print STDERR "                                   with the value 'value'\n";
    print STDERR "                                   You can use this option multiple times.\n";
    print STDERR "         -b [--browser] <path>     path to the browser to use for interactive mode\n";
    print STDERR "                                   if <path> is 'default', it tries to start your\n";
    print STDERR "                                   default browser.\n";
    print STDERR "\n";
    print STDERR "         -p [--list-parameters]    show list of parameters\n\n";
    print STDERR "             --xml-output          print XML output\n";
    print STDERR "         -L [--log] <file>         log all networktraffic to <file>\n";
    print STDERR "            [--no-proxy]           do not use proxie's\n";
    print STDERR "             --locale=<locale>     define a locale e.g. en-US.utf-8\n\n";
    
    print STDERR "Example:\n\n";
    print STDERR " suse_register.pl -n -a email=company\@example.com -a companyID=03474hdkndg3934957340\n";
    print STDERR "\n";
    exit 2;
}

#########################################################
### Main Program
#########################################################


my $result = GetOptions ("interactive|i"     => \$interactive,
                         "list-parameters|p" => \$listParams,
                         "product=s"         => \@comlineProducts,
                         "xml-output"        => \$xmlout,
                         "dumpfile=s"        => \$dumpfilehack,
                         "dumpxmlfile=s"     => \$dumpxmlfilehack,
                         "nozypp"            => \$nozypp,
                         "no-optional|n"     => \$nooptional,
                         "force-registration|f" => \$forcereg,
                         "no-hw-data"        => \$nohwdata,
                         "log|L=s"           => \$logfile,
                         "locale=s"          => \$locale,
                         "browser|b=s"       => \$browser,
                         "forceZmdConfiguration" => \$forceZmdConfiguration,
                         "no-proxy"          => \$noproxy,
                         "yast|y"            => \$yastcall,
                         "help|?|h"          => \$help,
                         "debug|d=i"         => \$debug,
                         "arg|a=s"           => \%args,
                         "extra-curl-options=s" => \@extraCurlOption,
                         "t"                 => \$time);

if ($help) 
{
    usage();
}
openlog("suse_register", "ndelay,pid", 'user');

if(-e $zmdInit && -e $rug) 
{
    $rugzmdInstalled = 1;
}

# lib64 hack
if(! -e $querypool) 
{
    $querypool = "/usr/lib64/zmd/query-pool";

    if(!-e $querypool)
    {
        logPrintExit("query-pool not found\n", 12);
    }    
}


# call this as soon as possible.
rugStart();

if (defined $logfile && $logfile ne "")
{
    open($LOGDESCR, ">> $logfile") or do 
    {
        if(!$yastcall)
        {
            logPrintExit("Cannot open logfile <$logfile>: $!\n", 12);
        }
        else
        {
            syslog("err", "Cannot open logfile <$logfile>: $!(yastcall ignoring error)");
            $LOGDESCR = undef;
        }
    };
    # $LOGDESCR is undef if no logfile is defined
    if(defined $LOGDESCR)
    {
        print $LOGDESCR "----- ".localtime()." ---------------------------------------\n";
    }
}

$ENV{'PATH'} = '/bin:/usr/bin:/sbin:/usr/sbin:/opt/kde3/bin:/opt/gnome/bin';

my @x = ();
foreach my $p (@comlineProducts)
{
    push @x, [$p, "0"];
}
@comlineProducts = @x;

readSystemValues();

print STDERR "interactive:       $interactive\n" if($debug);
print STDERR "list-parameters:   $listParams\n" if($debug);
print STDERR "product:           ".Data::Dumper->Dump([\@comlineProducts])."\n" if($debug);
print STDERR "xml-output:        $xmlout\n" if($debug);
print STDERR "dumpfile:          $dumpfilehack\n" if($debug);
print STDERR "dumpxmlfile:       $dumpxmlfilehack\n" if($debug);
print STDERR "nozypp:            $nozypp\n" if($debug);
print STDERR "no-optional:       $nooptional\n" if($debug);
print STDERR "forcereg:          $forcereg\n" if($debug);
print STDERR "no-hw-data:        $nohwdata\n" if($debug);
print STDERR "log:               ".(($logfile)?$logfile:"undef")."\n" if($debug);
print STDERR "locale:            ".(($locale)?$locale:"undef")."\n" if($debug);
print STDERR "browser:           ".(($browser)?$browser:"undef")."\n" if($debug);
print STDERR "forceZmdConfiguration: $forceZmdConfiguration\n" if($debug);
print STDERR "no-proxy:          $noproxy\n" if($debug);
print STDERR "yastcall:          $yastcall\n" if($debug);
print STDERR "arg: ".Data::Dumper->Dump([{%args}])."\n" if($debug);
print STDERR "extra-curl-option:".Data::Dumper->Dump([@extraCurlOption])."\n" if($debug);

print STDERR "URL:               $URL\n" if($debug);
print STDERR "listParams:        $URLlistParams\n" if($debug);
print STDERR "register:          $URLregister\n" if($debug);

$initnooptional = $nooptional;

my $iuri = URI->new($URL);

$initialDomain = $iuri->host;
$initialDomain =~ s/.+(\.[^.]+\.[^.]+)$/$1/;

print STDERR "initialDomain:     $initialDomain\n" if($debug);

if(exists $ENV{LANG} && $ENV{LANG} =~ /^([\w_]+)\.?/) 
{
    if(defined $1 && $1 ne "") 
    {
        $lang = $1;
        $lang =~ s/_/-/;
    }
}
elsif(exists $ENV{LANGUAGE} && $ENV{LANGUAGE} =~ /^([\w_]+)\.?/) 
{
    if(defined $1 && $1 ne "") 
    {
        $lang = $1;
        $lang =~ s/_/-/;
    }
}

if (defined $locale)
{
    my ($l, $e) = split(/\.|@/, $locale, 2);

    if (defined $l && $l ne "")
    {
        $l =~ s/_/-/;
        $lang = $l;
    }

    if (defined $e && $e ne "") 
    {        
        $encoding = $e;
    }
}

print STDERR "lang:              $lang\n" if($debug);

# set LANG to en_US to get the error messages in english
$ENV{LANG}     = "en_US";
$ENV{LANGUAGE} = "en_US";


listProducts();

if(@comlineProducts > 0)
{
    $products = intersection(\@comlineProducts, \@installedProducts);
    $products = intersection($products, \@serverKnownProducts);
}
else
{
    $products = intersection(\@installedProducts, \@serverKnownProducts);
}

if(@$products == 0)
{
    logPrintExit("No products to register\n", 100);
}

my $ret = 0;

if ($listParams)
{
    $ret = listParams();
}
else 
{
    $ret = register();
}

close $LOGDESCR if(defined $LOGDESCR);
closelog;

if($ret == 0 && $yastcall)
{
    foreach my $service (keys %$zmdConfig) 
    {
        next if($service eq "globalzmdoptions");
        
        print STDERR $zmdConfig->{$service}->{url}."\n";
    }
}

print STDERR "total time: ".(tv_interval($programStartTime))."\n" if($time);

exit $ret;

