Jacek Kowalski
2023-07-24 8cde7c538ea1d0d8aa51bcb3f2482ae4f19713ad
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php
class Fetch {
    static function ftp($url, $file = NULL) {
        $url = parse_url($url);
        if(!isset($url['scheme']) || $url['scheme'] != 'ftp') {
            throw new Exception('Only FTP URLs are supported');
        }
        if(!isset($url['host'])) {
            throw new Exception('Hostname not present in the URL');
        }
        if(!isset($url['path'])) {
            throw new Exception('Path component not present in the URL');
        }
        if(!isset($url['port'])) {
            $url['port'] = 21;
        }
        if(!isset($url['user'])) {
            $url['user'] = 'anonymous';
        }
        if(!isset($url['pass'])) {
            $url['pass'] = 'anonymous@mpk.jacekk.net';
        }
        if($file === NULL) {
            $file = basename($url['path']);
        }
        
        $localTime = -1;
        $localSize = -1;
        if(is_file($file)) {
            $localTime = filemtime($file);
            $localSize = filesize($file);
        }
        
        $ftp = FtpConnection::create($url['host'], $url['port'], $url['user'], $url['pass']);
        $remoteSize = $ftp->size($url['path']);
        $remoteTime = $ftp->mdtm($url['path']);
        
        $updated = FALSE;
        
        if($localTime >= $remoteTime && $localSize == $remoteSize) {
            return FALSE;
        }
        
        if(file_exists($file.'.tmp')) {
            unlink($file.'.tmp');
        }
        $ftp->get($file.'.tmp', $url['path'], FTP_BINARY);
        touch($file.'.tmp', $remoteTime);
        if(!rename($file.'.tmp', $file)) {
            throw new Exception('Temporary file rename failed');
        }
        
        return TRUE;
    }
    
    static function parse_http_headers($headers) {
        $hasHeader = FALSE;
        foreach($headers as $header) {
            if(substr($header, 0, 5) === 'HTTP/') {
                $code = substr($header, 9, 3);
                if($code === '304') {
                    return NULL;
                } elseif(substr($code, 0, 1) == '2') {
                    $hasHeader = TRUE;
                }
            } elseif($hasHeader && strtolower(substr($header, 0, 15)) === 'last-modified: ') {
                return strptime(substr($header, 15), 'D, d M Y H:i:s T');
            }
        }
        return FALSE;
    }
    
    static function generic($url, $file = NULL) {
        if($file === NULL) {
            $file = basename($url['url']);
        }
        
        $context = [];
        if(is_file($file)) {
            $file_date = filemtime($file);
            $context['http'] = [
                'header' => [
                    'If-Modified-Since: '.gmdate('D, d M Y H:i:s T', $file_date),
                ],
            ];
        }
        
        $data = file_get_contents($url, FALSE, stream_context_create($context));
        $remoteTime = FALSE;
        if(isset($http_response_header) && is_array($http_response_header)) {
            $remoteTime = self::parse_http_headers($http_response_header);
            if($remoteTime === NULL) {
                return FALSE;
            }
        }
        
        if($data === FALSE) {
            throw new Exception('URL fetch failed');
        }
        if(file_put_contents($file.'.tmp', $data) === FALSE) {
            throw new Exception('Temporary file creation failed');
        }
        if($remoteTime !== FALSE) {
            touch($file.'.tmp', $remoteTime);
        }
        if(!rename($file.'.tmp', $file)) {
            throw new Exception('Temporary file rename failed');
        }
        
        return TRUE;
    }
    
    static function auto($url, $file = NULL) {
        if($file === NULL) {
            $file = basename($url['url']);
        }
        if(substr($url, 0, 4) == 'ftp:') {
            return self::ftp($url, $file);
        }
        return self::generic($url, $file);
    }
}