Description: refactor copher for Sourceforge's changed interface
 This is a pre-0.2.1 patch from upstream
Origin: http://copher.cvs.sourceforge.net/viewvc/copher/copher/copher.pl?r1=1.21&r2=1.19
Bug: http://sourceforge.net/tracker/?func=detail&aid=2874738&group_id=137217&atid=738087
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=587061
Last-Update: 2010-06-25

--- copher-0.2.0.orig/copher.pl
+++ copher-0.2.0/copher.pl
@@ -19,6 +19,7 @@
 use WWW::Mechanize;
 use Carp;
 use File::Basename;
+use Cwd 'abs_path';
 
 ###########
 # 'Constants'
@@ -40,13 +41,17 @@
         login_form_action => '/account/login.php',
         projects => '/projects/',
         home => '',
+        # These seem to no longer exist as of 20091013.
         base_newpackage => '/project/admin/editpackages.php',
         base_editpackages => '/project/admin/editpackages.php',
         base_newrelease => '/project/admin/newrelease.php',
         base_editreleases => '/project/admin/editreleases.php',
         base_editrelease => '/project/admin/editreleases.php', # edit a single release
-        base_deleterelease => undef # does not exist in sourceforge
-    },
+        #
+        base_deleterelease => undef, # does not exist in sourceforge
+        base_filemanager => '/project/admin/explorer.php', # add e.g. ?group_id=137217
+        base_explorerajax => '/project/admin/explorer_ajax.php',
+},
     rubyforge => {
         login => '/account/login.php',
         login_form_action => '/account/login.php',
@@ -67,7 +72,8 @@
 my @sf_ftp_path = qw(incoming);     # path to enter before uploading file
 
 my $sf_frs = "frs.sourceforge.net";
-my $sf_frs_path = "uploads";
+my $sf_frs_path = "uploads"; # obsolete circa 20091013
+my $sf_frs_path_fmt = "/home/frs/project/%s/%s/%s/";
 
 ###########
 # To be set by the script when they're determined:
@@ -447,6 +453,7 @@
     print STDERR "Uploading files...\n";
     &upload_files(\%release_files);
 }
+exit; ##HERE
 #print "Sleeping for 10 seconds to allow sourceforge to notice files...\n";
 #sleep 10;
 
@@ -508,97 +515,6 @@
     }
 }
 
-if (! $package_id) {
-    print STDERR "No package_id specified, fetching Edit Packages ('File Releases') page with group_id=$group_id... ";
-    my $response = &editpackages_php($mech, $group_id);
-    &save_page(sprintf("%02d-editpackages.html", $debug_pagenum++)) if $debug;
-    print STDERR &check_response($mech, $response, 1) . "\n";
-    
-    my @packages = &get_packages($mech);
-    foreach my $pkg (@packages) {
-        print STDERR "Package: name: " . $pkg->{package_name} .', package_id: '. $pkg->{package_id} .', group_id: '. $pkg->{group_id} ."\n";
-        if ($pkg->{package_name} eq $package_name) {      # good!
-            $package_id = $pkg->{package_id};
-            # ensure $pkg->{group_id} eq $group_id? it "should"
-            unless ($pkg->{group_id} eq $group_id) {
-                warn $pkg->{group_id} . " doesn't match known group ID " . $group_id . "\n";
-            }
-        }
-    }
-    if (!$package_id) {
-        print STDERR "Couldn't find a package with package_name $package_name\n";
-        # future versions will be able to handle creation of new packages [todo]
-        
-        #print STDERR "Creating a new package...\n";
-        exit 1; # not yet
-    }
-}
-
-if (! $release_id) {
-    print STDERR "No release_id specified, fetching Edit Releases page with group_id=$group_id, package_id=$package_id... ";
-    # or "Fetching list of file releases for $package_name (group_id=$group_id, package_id=$package_id)... ";
-    my $response = &editreleases_php($mech, $group_id, $package_id);
-    &save_page(sprintf("%02d-editreleases.html", $debug_pagenum++)) if $debug;
-    print STDERR &check_response($mech, $response, 1) . "\n";
-    
-    my @releases = &get_releases($mech);
-    foreach my $rls (@releases) {
-        debug("Release: name: " . $rls->{release_name} .', release_id: '. $rls->{release_id} .', package_id: '. $rls->{package_id} ."\n");
-        if ($rls->{release_name} eq $release_name) {
-            $release_id = $rls->{release_id};
-            print STDERR "Found release with name $release_name, release_id is $release_id. Using...\n";
-            # sanity-check group_id and package_id
-            if ($group_id != $rls->{group_id} or $package_id != $rls->{package_id}) {
-                warn "group_id or package_id mismatch! $rls->{group_id} != $group_id or $rls->{package_id} != $package_id. Probably will fail."
-            }
-            #last;
-        }
-    }
-
-    if (!$release_id) {
-        if ($opt{exists}) {
-            print STDERR "Couldn't find a release with release_name $release_name, exiting (omit -E option to create new release)...\n";
-            &close_copher(1);
-        } else {
-            print STDERR "Couldn't find a release with release_name $release_name, will create one...\n";
-        }
-    }
-}
-
-if (! $release_id) {
-    print STDERR "No release_id specified, fetching New Release page... ";
-    my $response = &newrelease_php($mech, $group_id, $package_id);
-    &save_page(sprintf("%02d-newrelease.html", $debug_pagenum++)) if $debug;
-    print STDERR &check_response($mech, $response, 1) . "\n";
-    
-    print STDERR "Adding new release (submitting form on New Release page)... ";
-    my $response =  &add_release($mech, $group_id, $package_id, $release_name);
-    $new_release = 1;
-    &save_page(sprintf("%02d-added_release.html", $debug_pagenum++)) if $debug;
-    print STDERR &check_response($mech, $response, 1) . "\n";
-
-    if ($site eq 'rubyforge') {
-        # rubyforge doesn't automatically load the editrelease page next
-        if ($mech->content =~ m|<a href="$urls{$site}->{base_editrelease}.*?release_id=(\d+)|) {
-            $release_id = $1;
-            print STDERR "Fetching Edit Release page, editing release $release_id... ";
-            my $response = &editrelease_php($mech, $group_id, $package_id, $release_id);
-            &save_page(sprintf("%02d-editrelease.html", $debug_pagenum++)) if $debug;
-            print STDERR &check_response($mech, $response, 1) . "\n";
-        } else {
-            warn "unable to find release_id in output of add_release() -- see out.html\n";
-            &close_copher;
-        }
-    }
-} else {
-    print STDERR "Fetching Edit Release page, editing release $release_id... ";
-    my $response = &editrelease_php($mech, $group_id, $package_id, $release_id);
-    &save_page(sprintf("%02d-editrelease.html", $debug_pagenum++)) if $debug;
-    print STDERR &check_response($mech, $response, 1) . "\n";
-}
-
-
-# Now add files to the release
 my %release = (
     id => $release_id,
     date => $release_date || $current_date,
@@ -609,68 +525,169 @@
     files => \%release_files
     );
 
-if ($site eq 'rubyforge' && $new_release) {
-    print STDERR "Skipping Step 1 -- already done.\n";
+if ($site eq 'sourceforge') {
+#        notes_file => $notes_file,
+#        changelog_file => $changelog_file,
+#        files => \%release_files
+
+
 } else {
-    print STDERR "Carrying out Step 1: Edit Existing Release... ";
-    my $response = &edit_release_step1($mech, $group_id, $package_id, \%release);
-    &save_page(sprintf("%02d-step1-edit_existing_release.html", $debug_pagenum++)) if $debug;
-    print STDERR &check_response($mech, $response, 0) . "\n";
-}
+    if (! $package_id) {
+        print STDERR "No package_id specified, fetching Edit Packages ('File Releases') page with group_id=$group_id... ";
+        my $response = &editpackages_php($mech, $group_id);
+        &save_page(sprintf("%02d-editpackages.html", $debug_pagenum++)) if $debug;
+        print STDERR &check_response($mech, $response, 1) . "\n";
+
+        my @packages = &get_packages($mech);
+        foreach my $pkg (@packages) {
+            print STDERR "Package: name: " . $pkg->{package_name} .', package_id: '. $pkg->{package_id} .', group_id: '. $pkg->{group_id} ."\n";
+            if ($pkg->{package_name} eq $package_name) { # good!
+                $package_id = $pkg->{package_id};
+                # ensure $pkg->{group_id} eq $group_id? it "should"
+                unless ($pkg->{group_id} eq $group_id) {
+                    warn $pkg->{group_id} . " doesn't match known group ID " . $group_id . "\n";
+                }
+            }
+        }
+        if (!$package_id) {
+            print STDERR "Couldn't find a package with package_name $package_name\n";
+            # future versions will be able to handle creation of new packages [todo]
+
+            #print STDERR "Creating a new package...\n";
+            exit 1;             # not yet
+        }
+    }
 
-if (%release_files) {
-    if ($site eq 'rubyforge' && $new_release && @release_files == 1) {
-        # only file already added in add_release()
-        print STDERR "Skipping Step 2 (Add Files To This Releases) -- already done.";
-    } elsif ($site eq 'rubyforge' && $opt{noupload}) {
-        # since uploading is done in step2 in rubyforge, skip step2 if noupload is set
-    } else {
-        my $added_all = 0;
-        while ($site ne 'rubyforge' || !$added_all) { # run edit_release_step2 once per file for rubyforge
-            $added_all = 1;
-            for my $f (keys %release_files) {
-                unless ($release_files{$f}->{added}) {
-                    $added_all = 0; # (at least) one file left to add
-                    last;
+    if (! $release_id) {
+        print STDERR "No release_id specified, fetching Edit Releases page with group_id=$group_id, package_id=$package_id... ";
+        # or "Fetching list of file releases for $package_name (group_id=$group_id, package_id=$package_id)... ";
+        my $response = &editreleases_php($mech, $group_id, $package_id);
+        &save_page(sprintf("%02d-editreleases.html", $debug_pagenum++)) if $debug;
+        print STDERR &check_response($mech, $response, 1) . "\n";
+
+        my @releases = &get_releases($mech);
+        foreach my $rls (@releases) {
+            debug("Release: name: " . $rls->{release_name} .', release_id: '. $rls->{release_id} .', package_id: '. $rls->{package_id} ."\n");
+            if ($rls->{release_name} eq $release_name) {
+                $release_id = $rls->{release_id};
+                print STDERR "Found release with name $release_name, release_id is $release_id. Using...\n";
+                # sanity-check group_id and package_id
+                if ($group_id != $rls->{group_id} or $package_id != $rls->{package_id}) {
+                    warn "group_id or package_id mismatch! $rls->{group_id} != $group_id or $rls->{package_id} != $package_id. Probably will fail."
                 }
+                #last;
+            }
+        }
+
+        if (!$release_id) {
+            if ($opt{exists}) {
+                print STDERR "Couldn't find a release with release_name $release_name, exiting (omit -E option to create new release)...\n";
+                &close_copher(1);
+            } else {
+                print STDERR "Couldn't find a release with release_name $release_name, will create one...\n";
             }
-            last if $added_all;
+        }
+    }
 
-            print STDERR "Carrying out Step 2: Add Files To This Releases... \n";
-            my $response = &edit_release_step2($mech, $group_id, $package_id, \%release);
-            &save_page(sprintf("%02d-step2-add_files.html", $debug_pagenum++)) if $debug;
-            print STDERR &check_response($mech, $response, 0) . "\n";
-            if ($mech->content =~ /Error.*That filename already exists in this project space/) {
-                # TODO: optionally delete and re-add the file? the user might have updated it...
-                #       should actually check BEFORE trying to add the file, though...
-                print STDERR "Error: File already added...\n";
+    if (! $release_id) {
+        print STDERR "No release_id specified, fetching New Release page... ";
+        my $response = &newrelease_php($mech, $group_id, $package_id);
+        &save_page(sprintf("%02d-newrelease.html", $debug_pagenum++)) if $debug;
+        print STDERR &check_response($mech, $response, 1) . "\n";
+
+        print STDERR "Adding new release (submitting form on New Release page)... ";
+        my $response =  &add_release($mech, $group_id, $package_id, $release_name);
+        $new_release = 1;
+        &save_page(sprintf("%02d-added_release.html", $debug_pagenum++)) if $debug;
+        print STDERR &check_response($mech, $response, 1) . "\n";
+
+        if ($site eq 'rubyforge') {
+            # rubyforge doesn't automatically load the editrelease page next
+            if ($mech->content =~ m|<a href="$urls{$site}->{base_editrelease}.*?release_id=(\d+)|) {
+                $release_id = $1;
                 print STDERR "Fetching Edit Release page, editing release $release_id... ";
                 my $response = &editrelease_php($mech, $group_id, $package_id, $release_id);
                 &save_page(sprintf("%02d-editrelease.html", $debug_pagenum++)) if $debug;
                 print STDERR &check_response($mech, $response, 1) . "\n";
+            } else {
+                warn "unable to find release_id in output of add_release() -- see out.html\n";
+                &close_copher;
             }
         }
+    } else {
+        print STDERR "Fetching Edit Release page, editing release $release_id... ";
+        my $response = &editrelease_php($mech, $group_id, $package_id, $release_id);
+        &save_page(sprintf("%02d-editrelease.html", $debug_pagenum++)) if $debug;
+        print STDERR &check_response($mech, $response, 1) . "\n";
     }
-}
 
-if ($site eq 'rubyforge' && $new_release && @release_files == 1) {
-    # only file already added in add_release()
-    print STDERR "Skipping Step 3 (Edit Files To This Releases) -- already done.";
-} else {
-    print STDERR "Carrying out Step 3: Edit Files In This Release...\n";
-    my $value = &edit_release_step3($mech, $group_id, $package_id, \%release);
-    &save_page(sprintf("%02d-step3-edit_files.html", $debug_pagenum++)) if $debug;
-    if (!$value) {
-        print STDERR "Error doing step 3. $value\n";
+
+# Now add files to the release
+
+    if ($site eq 'rubyforge' && $new_release) {
+        print STDERR "Skipping Step 1 -- already done.\n";
+    } else {
+        print STDERR "Carrying out Step 1: Edit Existing Release... ";
+        my $response = &edit_release_step1($mech, $group_id, $package_id, \%release);
+        &save_page(sprintf("%02d-step1-edit_existing_release.html", $debug_pagenum++)) if $debug;
+        print STDERR &check_response($mech, $response, 0) . "\n";
     }
-}
 
-if ($opt{notice} && $site eq 'sourceforge') { # rubyforge does not have this option
-    # Step 4. Email Release Notice
-    print STDERR "Carrying out Step 4: Email Release Notice...\n";
-    my $response = &edit_release_step4($mech, $group_id, $package_id, \%release);
-    &save_page(sprintf("%02d-step4-email_release_notice.html", $debug_pagenum++)) if $debug;
-    print STDERR &check_response($mech, $response, 0) . "\n";
+    if (%release_files) {
+        if ($site eq 'rubyforge' && $new_release && @release_files == 1) {
+            # only file already added in add_release()
+            print STDERR "Skipping Step 2 (Add Files To This Releases) -- already done.";
+        } elsif ($site eq 'rubyforge' && $opt{noupload}) {
+            # since uploading is done in step2 in rubyforge, skip step2 if noupload is set
+        } else {
+            my $added_all = 0;
+            while ($site ne 'rubyforge' || !$added_all) { # run edit_release_step2 once per file for rubyforge
+                $added_all = 1;
+                for my $f (keys %release_files) {
+                    unless ($release_files{$f}->{added}) {
+                        $added_all = 0; # (at least) one file left to add
+                        last;
+                    }
+                }
+                last if $added_all;
+
+                print STDERR "Carrying out Step 2: Add Files To This Releases... \n";
+                my $response = &edit_release_step2($mech, $group_id, $package_id, \%release);
+                &save_page(sprintf("%02d-step2-add_files.html", $debug_pagenum++)) if $debug;
+                print STDERR &check_response($mech, $response, 0) . "\n";
+                if ($mech->content =~ /Error.*That filename already exists in this project space/) {
+                    # TODO: optionally delete and re-add the file? the user might have updated it...
+                    #       should actually check BEFORE trying to add the file, though...
+                    print STDERR "Error: File already added...\n";
+                    print STDERR "Fetching Edit Release page, editing release $release_id... ";
+                    my $response = &editrelease_php($mech, $group_id, $package_id, $release_id);
+                    &save_page(sprintf("%02d-editrelease.html", $debug_pagenum++)) if $debug;
+                    print STDERR &check_response($mech, $response, 1) . "\n";
+                }
+            }
+        }
+    }
+
+    if ($site eq 'rubyforge' && $new_release && @release_files == 1) {
+        # only file already added in add_release()
+        print STDERR "Skipping Step 3 (Edit Files To This Releases) -- already done.";
+    } else {
+        print STDERR "Carrying out Step 3: Edit Files In This Release...\n";
+        my $value = &edit_release_step3($mech, $group_id, $package_id, \%release);
+        &save_page(sprintf("%02d-step3-edit_files.html", $debug_pagenum++)) if $debug;
+        if (!$value) {
+            print STDERR "Error doing step 3. $value\n";
+        }
+    }
+
+    if ($opt{notice} && $site eq 'sourceforge') { # rubyforge does not have this option
+        # (apparently sourceforge doesn't either, any more -20091013)
+        # Step 4. Email Release Notice
+        print STDERR "Carrying out Step 4: Email Release Notice...\n";
+        my $response = &edit_release_step4($mech, $group_id, $package_id, \%release);
+        &save_page(sprintf("%02d-step4-email_release_notice.html", $debug_pagenum++)) if $debug;
+        print STDERR &check_response($mech, $response, 0) . "\n";
+    }
 }
 
 print STDERR "Finished!\n";
@@ -760,7 +777,43 @@
 
     # Warning: any/some of these may be empty
     return $mech->get($base_url."?group_id=$group_id&package_id=$package_id");
-                   }
+}
+
+# TODO:
+# for release notes file, call explorerajax_php
+#   with release_noteable => 1
+# for each (other) file, call explorerajax_php
+#   with release_notable => 0,
+#        release_notes => $release_notes_filename (path from FRS root, e.g. /$package_name/$release_name/notes)
+sub explorerajax_php($$$$@) {
+    my ($mech, $group_id, $package_id, $filename);
+    ($mech, $group_id, $package_id, $filename, %_) = @_;
+
+    my $base_url = $sites{$site}.$urls{$site}->{base_explorerajax};
+    my $url = $base_url."?group_id=$group_id";
+
+    my %defaults = (
+        operation => 'save_settings',
+        save_settings => 'Save',
+        original_filename => $filename,
+        filename => $filename,
+        filepath => "/$package_name/$release_name/$filename",
+        );
+    for (keys %defaults) {
+        $_{$_} = $defaults{$_} unless defined $_{$_};
+    }
+
+    for (keys %_) {
+        $url .= "&$_=$_{$_}";
+    }
+
+    # Example URL:
+    # https://sourceforge.net/project/admin/explorer_ajax.php?group_id=137217&default_download=&filepath=%2Fcopher%2Fcopher-0.2.1%2Fcopher-0.2.1.tar.gz&operation=save_settings&original_filename=copher-0.2.1.tar.gz&filename=copher-0.2.1.tar.gz&download_label=&release_notable=1&default_linux=linux&default_mac=mac&default_windows=windows&default_bsd=bsd&default_solaris=solaris&default_others=others&release_notes=&save_settings=Save
+
+    # returned content should be: { "val": 100, "message": "OK" }
+    return $mech->get($url);
+}
+
 
 # Get a list of packages from editpackages.php page (which must already be loaded in $mech)
 sub get_packages ($) {
@@ -900,19 +953,42 @@
     my $release_files = shift;
     my @fullpaths = map { $release_files->{$_}->{'fullpath'} } keys %$release_files;
 
-    my $obj = File::Rsync->new( { archive => 1, rsh => 'ssh' } );
+    my $obj = File::Rsync->new( { times => 1, rsh => 'ssh', relative => 1, 'omit-dir-times' => 1} );
+
+    require File::Temp;
+    my $tmpdir = File::Temp::tempdir();#CLEANUP => 1) or die "Failed to create temporary directory: $!\n";
+    print STDERR "\$tmpdir=$tmpdir\n";
+    symlink('.', "$tmpdir/$package_name") or die "Failed to create package symlink in $tmpdir: $!\n";
+
+    my $srcpath = "$tmpdir/./$package_name/$release_name";
+    my $dstpath = sprintf($sf_frs_path_fmt, 
+                       substr($project_name, 0, 1),
+                       substr($project_name, 0, 2),
+                       $project_name
+        );
+    print "src path is $srcpath\n";
+    print "dst path is $dstpath\n";
+
+    # e.g. final destination path: /home/frs/project/c/co/copher/copher/copher-0.0.0/
 
     foreach my $file (@fullpaths) {
         print STDERR "Uploading $file... ";
-        if ($obj->exec( { src => $file, dest => "$user{loginname}\@$sf_frs:$sf_frs_path" } )) {
+        my $dirname = abs_path(dirname($file));
+        my $basename = basename $file;
+        unlink("$tmpdir/$release_name") if -l "$tmpdir/$release_name";
+        symlink("$dirname", "$tmpdir/$release_name") or die "Failed to create release symlink in $tmpdir: $!\n";
+
+        my $cmd = $obj->getcmd( { src => "$srcpath/$basename", dest => "$user{loginname},$project_name\@$sf_frs:$dstpath" });
+        #print STDERR "\n  rsync command: @$cmd\n";
+        
+        if ($obj->exec( { src => "$srcpath/$basename", dest => "$user{loginname},$project_name\@$sf_frs:$dstpath" } )) {
             print STDERR "Done\n";
         } else {
             print STDERR "Failed: $!.\n";
         }
-
         $release_files->{basename($file)}->{'uploaded'} = 1;     # keep track of which files we've uploaded
     }
-                        }
+}
 
 sub upload_files_scp ($) {
     # sourceforge says scp is not supported; it doesn't work
@@ -983,7 +1059,7 @@
     # https://frs.sourceforge.net/U/US/USERNAME/uploads
     # HTTP::Webdav
     print STDERR "ERROR: webdav uploader not yet implemented\n";
-                         }
+}
 
 # Add a new release by submitting the form on the newrelease.php page
 # Pre-condition: $mech->content is the newrelease.php page with correct package_id and group_id
