--- loncom/homework/daxesave.pm	2015/12/10 16:26:43	1.2
+++ loncom/homework/daxesave.pm	2023/08/29 01:43:35	1.8
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Convert and save a problem from Daxe.
 #
-# $Id: daxesave.pm,v 1.2 2015/12/10 16:26:43 damieng Exp $
+# $Id: daxesave.pm,v 1.8 2023/08/29 01:43:35 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -28,8 +28,9 @@
 ###
 
 package Apache::daxesave;
+use strict;
 
-use Apache::Constants;
+use Apache::Constants qw(:common);
 use Apache::lonnet;
 use Try::Tiny;
 use File::Copy;
@@ -43,17 +44,32 @@ sub handler {
     
     $request->content_type('text/plain');
     
-    my $path = $env{'form.path'}; # should be in the form "/daxeopen/priv/..."
+    # path should be in the form "/daxeopen/priv/..."
+    # or "/daxeopen/uploaded/$cdom/$cnum/(docs|supplemental)/(default|\d+)/\d+/"
+    my $path = $env{'form.path'};
     $path =~ s/^\/daxeopen//;
     
-    my $allowed;
-    my ($ownername,$ownerdom,$ownerhome) = 
-        &Apache::lonnet::constructaccess($path, 'setpriv');
-    if (($ownername ne '') && ($ownerdom ne '') && ($ownerhome ne '')) {
-        unless ($ownerhome eq 'no_host') {
-            my @hosts = &Apache::lonnet::current_machine_ids();
-            if (grep(/^\Q$ownerhome\E$/,@hosts)) {
-                $allowed = 1;
+    my $allowed = 0;
+    my ($cdom,$cnum);
+    if ($path =~ m{^/priv/}) {
+        my ($ownername,$ownerdom,$ownerhome) = 
+            &Apache::lonnet::constructaccess($path);
+        if (($ownername ne '') && ($ownerdom ne '') && ($ownerhome ne '')) {
+            unless ($ownerhome eq 'no_host') {
+                my @hosts = &Apache::lonnet::current_machine_ids();
+                if (grep(/^\Q$ownerhome\E$/,@hosts)) {
+                    $allowed = 1;
+                }
+            }
+        }
+    } elsif ($path =~ m|^/uploaded/|) {
+        if ($env{'user.name'} ne '' && $env{'user.domain'} ne '' && $env{'request.course.id'}) {
+            $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+            if ($path =~ m|^/uploaded/\Q$cdom\E/\Q$cnum\E/| && $path !~ /\.\./) {
+                if (&Apache::lonnet::allowed('mdc', $env{'request.course.id'})) {
+                    $allowed = 1;
+                }
             }
         }
     }
@@ -64,26 +80,75 @@ sub handler {
         return OK;
     }
 
-    my $newpath = &Apache::lonnet::filelocation('', $path);
-
-    my $contents = $env{'form.file'};
-    
-    try {
-        $contents = &Apache::xml_to_loncapa::convert_file($contents);
-    } catch {
-        $request->print("error\nconvert failed for $path: $_");
-        return OK;
-    };
-    
-    my $filebak = $newpath.".bak";
-    if (-e $newpath) {
-        copy($newpath, $filebak); # errors ignored
+    if ($path =~ m{^/priv/}) {
+        my $newpath = &Apache::lonnet::filelocation('', $path);
+        my $contents = $env{'form.file'};
+    
+        my $mode;
+        if ($path =~ /\.(task|problem|exam|quiz|assess|survey|library|xml|html|htm|xhtml|xhtm)$/) {
+            try {
+                $contents = &Apache::xml_to_loncapa::convert_file($contents);
+            } catch {
+                $request->print("error\nconvert failed for $path: $_");
+                return OK;
+            };
+            $mode = '>:encoding(UTF-8)';
+        } else {
+            $mode = '>';
+        }
+  
+        my $filebak = $newpath.".bak";
+        if (-e $newpath) {
+            copy($newpath, $filebak); # errors ignored
+        }
+        if (open(my $out, $mode, $newpath)) {
+            print $out $contents;
+            close($out);
+            $request->print("ok\n");
+        } else {
+            $request->print("error\nFailed to open file to save $path");
+        }
+    } elsif ($path =~ m{^/uploaded/}) {
+        my ($unauthorized,$unsupported);
+        if ($path =~ m{^\Q/uploaded/$cdom/$cnum/\E(docs|supplemental)/(default|\d+)/(\d+)/(.+)$}) {
+            my ($type,$folder,$rid,$fname) = ($1,$2,$3,$4);
+            my $referrer = $request->headers_in->{'Referer'};
+            if ($referrer =~ m{\Qfile=/daxeopen/uploaded/$cdom/$cnum/$type/$folder/$rid/\E}) {
+                if ($fname =~ /\.(html|htm|xhtml|xhtm)$/) {
+                    try {
+                        $env{'form.file'} = &Apache::xml_to_loncapa::convert_file($env{'form.file'});
+                    } catch {
+                        $request->print("error\nconvert failed for $fname: $_");
+                        return OK;
+                    }
+                } elsif ($fname =~ /\.(task|problem|exam|quiz|assess|survey|library|xml)$/) {
+                    $unsupported = $1;
+                }
+                unless ($unsupported) {
+                    my $url = &Apache::lonnet::userfileupload('file','daxesave',"$type/$folder/$rid",
+                                                               undef,undef,undef,$cnum,$cdom);
+                    if ($url =~ m{^/uploaded/$cdom/$cnum/$type/$folder/$rid/}) {
+                        $request->print("ok\n");
+                    } else {
+                        $request->print("error\nFailed to save uploaded file: $fname");
+                    }
+                }
+            } else {
+                $unauthorized = 1;
+            }
+        } else {
+            $unauthorized = 1;
+        }
+        if ($unauthorized) {
+            $request->log_reason("Unauthorized path: $path", $path);
+            $request->print("error\nUnauthorized path: $path");
+            $request->status(403);
+        } elsif ($unsupported) {
+            $request->log_reason("File extension: $unsupported -- not allowed for upload to course", $path);
+            $request->print("error\nFile extension: $unsupported -- not allowed for upload to course");
+            $request->status(403);
+        }
     }
-    open my $out, '>:encoding(UTF-8)', $newpath;
-    print $out $contents;
-    close $out;
-    
-    $request->print("ok\n");
     return OK;
 }