Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 9 of 9
  1. #1
    Regular Coder
    Join Date
    Jan 2005
    Posts
    189
    Thanks
    6
    Thanked 0 Times in 0 Posts

    Basic wrapper script examples?

    I've been doing a bit of searching online and can find a lot of explanations on "how" the functionality of a wrapper script works, but can't really find any coding examples that would be helpful for a PHP novice like myself.

    My issue is pretty simple I think. I have a password protected area for members only and a directory listing script that shows all files users can download in the downloads folder, but once you have the file URL, anyone can access that file through a browser window without authentication. I'd like to put a basic check in place - download.php?file= and then run a check to see if the user is logged in and serve up the file. I have an authentication function but I'm not sure what to do next. It would be serving up things like word docs/pdfs/xls/ppt files mainly, if that makes any difference.

    Does anyone have examples or know where I might be able to find a script. I can't imagine it's too complicated?

  • #2
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,994
    Thanks
    4
    Thanked 2,662 Times in 2,631 Posts
    This is a simple task yes.
    Effectively, you know what files the user can access, so when you present them with the download.php script, you take the file given in the $_GET['file'], and you double check that they can download it. Then you simply push the headers needed to force the download.
    Here's the link for the header documentation; the first example shows how to prompt the download: http://ca2.php.net/manual/en/function.header.php, I would recommend as well adding a header for the filesize: header('Content-length: ' . filesize($theFile));. I'd also recommend that the file's be located above the public_html so they cannot be directly accessed.

    On a side note, the wrapper is a bit ambiguous (although fits the context). It is also used to describe adapters, particularly with objects. So if I needed to have both hamburger and cheeseburger, instead of writing two separate classes to represent both, I would write a wrapper for hamburger that contains and describes itself as a cheeseburger.
    PHP Code:
    interface IHamburger
    {
        public function 
    getToppings();
    }

    class 
    Hamburger implements IHamburger
    {
        public function 
    getToppings()
        {
            return array(
    'Ketchup');
        }
    }

    class 
    CheeseBurgerWrapper implements IHamburger
    {
        private 
    $hamburger;
        public function 
    __construct(IHamburger $hamburger)
        {
            
    $this->hamburger $hamburger;
        }
        public function 
    getToppings()
        {
            return 
    array_merge(array('cheese'), $this->hamburger->getToppings());
        }
    }

    $burger = new Hamburger();
    printf('$burger has toppings: %s' PHP_EOLprint_r($burger->getToppings(), true));
    $burger = new CheeseBurgerWrapper($burger);
    printf('$burger has toppings: %s' PHP_EOLprint_r($burger->getToppings(), true)); 
    For a rather rudimentary example.

  • #3
    Regular Coder
    Join Date
    Jan 2005
    Posts
    189
    Thanks
    6
    Thanked 0 Times in 0 Posts
    Honestly, that code just kind of confused me more. Currently I have this, but I keep getting the error message "ERROR: You do not have permission to access this link."

    my global.php has the useronly() function in it that checks if they are logged in, I know that part works on the rest of the site if you want to view a page. The website is at /public_html/site/ and the downloads folder I have created is in the same level as /public_html/ (so below the /site directory). I'm not sure why it's not authenticating/letting me download the file though. (File is just called "test.pdf")

    PHP Code:
    <?php
    include('inc/global.php');
    ?>

    <?php
    if( !empty( $_GET['file'] ) )
    {
      
    // check if user is logged    
      
    if( userOnly() )
      {
        
    $filename preg_replace'#[^-\w]#'''$_GET['file'] );
        
    $downloadfile "{$_SERVER['DOCUMENT_ROOT']}/downloads/{$filename}";
        if( 
    file_exists$downloadfile ) )
        {
          
    header'Cache-Control: public' );
          
    header'Content-Description: File Transfer' );
          
    header"Content-Disposition: attachment; filename={$downloadfile}" );
          
    header'Content-Type: application/x download' );
          
    header'Content-Transfer-Encoding: binary' );
          
    readfile$downloadfile );
          exit;
        }
      }
    }
    die( 
    "ERROR: You do not have permission to access this link." );
    ?>
    Last edited by a4udi; 12-14-2012 at 08:52 PM.

  • #4
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,994
    Thanks
    4
    Thanked 2,662 Times in 2,631 Posts
    That example was for what the wrapper/adapter meaning is in the object oriented concepts (ie: a decorator pattern). It was just for the side note.
    The very first thing you need to do is remove this:
    PHP Code:
    ?>

    <?php
    That creates whitespace and without ini controlled output buffering that will also flush the headers. So the download headers wouldn't do a thing except throw an error (ie: headers already sent error since output is flushed).

    Add more else clauses for the branches. Try not to put a reliance on the exit as the control mechanism for whether you issue a die or not (since as it is it will always execute by look of logic flow barring the exit call).
    PHP Code:
    if (!empty($_GET['file']))
    {
        if (
    userOnly())
        {
            if (
    file_exists($downloadfile))
            {
                
    // download
            
    }
            else
            {
                die(
    'The file you have specified does not exist');
            }
        }
        else
        {
            die(
    'You do no have permission to access this link.');
        }
    }
    else
    {
        die(
    'No file provided.');

    Add the necessary branches to determine where you are (or walk it through a debugger of course). This will tell you which statement failed as you cannot tell from the above if the problem is that $_GET['file'] is empty, the userOnly() is false, or if file_exists is false.
    Otherwise, aside from the whitespace you need to remove the headers are correct. You may want to provide the actual content type of the file, or even just application/force-download instead. You should add the filesize so they get the nice estimated download time bar.

  • #5
    Regular Coder
    Join Date
    Jan 2005
    Posts
    189
    Thanks
    6
    Thanked 0 Times in 0 Posts
    Okay thanks, I will try those modifications!

    You may want to provide the actual content type of the file, or even just application/force-download instead. You should add the filesize so they get the nice estimated download time bar.
    This was something I wasn't clear on in the examples I saw either. Some of them would specify a file type and size, but I could literally have 1000's of files being downloaded of various sizes and types (mostly MS Office extensions) but nevertheless, I assume you would have to add all these file types if you wanted just specific ones?

  • #6
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,994
    Thanks
    4
    Thanked 2,662 Times in 2,631 Posts
    No, you an scan the filetype easily if you have the finfo and a mime.magic. Look into using the finfo here: http://php.net/manual/en/function.finfo-open.php
    You can manually associate as well, which is a lot more work.
    You can also just force something simple like the force-download. You don't know what it is, so you just give it to the client to work out.

  • #7
    Regular Coder
    Join Date
    Jan 2005
    Posts
    189
    Thanks
    6
    Thanked 0 Times in 0 Posts
    Fou-Lu, thank you for your help!

    I have almost everything functioning correctly except my dynamic links to the correct files paths are slightly off. I will try to explain briefly.

    The links just go to "download.php?file="
    and then the download.php file contains a URL
    $downloadfile = "/home/mysite/downloads/" . $_GET['file'];

    So everything works perfectly if my files are in the /downloads/ directory. The problem is that I have files in subdirectories of /downloads/ as well which I want to link to.

    Part of my script on the download pages generates a dynamic link to the current directory you are browsing:
    For example:
    $filepath = ../../../downloads/archive/2012/12/
    but if you click the link it just takes you to /home/mysite/downloads/

    What I'm trying to accomplish is to have a variable so I can take the /archive/2012/12/ (or whatever that dynamic link is) and add it to the $downloadfile variable in download.php

    So the link would be $downloadfile = "home/mysite/downloads/ . $filepath . $_GET['file'];

    So I'm assuming I need to take that $filepath variable and somehow just remove "../../../downloads/" from it?

  • #8
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,994
    Thanks
    4
    Thanked 2,662 Times in 2,631 Posts
    I don't recommend adding any pathing information as a part of the request for the downloaded file. The problem is you need to take extra care when accessing since relative paths could be given. Ultimately it will defeat the purpose of the download script as well since they can see where it goes, and you cannot rely on recursive checks in case there is a filename collision in different directories which may serve the wrong file.

    What I would recommend is using a database. The two most important parts of it will be the fileid (an integer would suffice), and a filepath. You can take the id provided in the ?file=114 (for example) querystring, and retrieve the path from the database to serve. This way you can randomize the name in any way you want to minimize potential collisions, and you will know right away if you use a unique for the filepath if its already taken (and therefore try again before creating it). Other data to store in the db would be the original filename and the content type, both of which are useful for generating the download (but irrelevant in storage).

  • #9
    Regular Coder
    Join Date
    Jan 2005
    Posts
    189
    Thanks
    6
    Thanked 0 Times in 0 Posts
    Edited... I think I just found an alternative way to solve this problem!

    Again, thanks so much for your guidance... it helped with some ideas to work through this.
    Last edited by a4udi; 12-18-2012 at 08:32 PM.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •