~cpick/mongrel2/release

« back to all changes in this revision

Viewing changes to src/dir.c

  • Committer: Chris Pick
  • Date: 2013-06-30 16:39:57 UTC
  • mfrom: (1106.1.15)
  • Revision ID: git-v1:ec39967acb6bc9867ed9b9dc3774304ca6b9c294
Merge tag 'v1.8.1' into debian

Hotfix for github issue 148

Show diffs side-by-side

added added

removed removed

Lines of Context:
61
61
    "\r\n\r\n";
62
62
 
63
63
const char *DIR_REDIRECT_FORMAT = "HTTP/1.1 301 Moved Permanently\r\n"
64
 
    "Location: http://%s%s/\r\n"
 
64
    "Location: //%s%s/\r\n"
65
65
    "Content-Length: 0\r\n"
66
66
    "Server: " VERSION
67
67
    "\r\n\r\n";
68
68
 
69
69
// TODO: confirm that we are actually doing the GMT time right
70
 
const char *RFC_822_TIME = "%a, %d %b %Y %H:%M:%S GMT";
 
70
static const char *RFC_822_TIME = "%a, %d %b %Y %H:%M:%S GMT";
71
71
 
72
72
static int filerecord_cache_lookup(void *data, void *key) {
73
73
    bstring request_path = (bstring) key;
80
80
    FileRecord_release((FileRecord *) data);
81
81
}
82
82
 
 
83
static inline int get_file_real_size(FileRecord *fr)
 
84
{
 
85
    // TODO: this is the total suck we'll redesign this away
 
86
    int fd = open(bdata(fr->full_path), O_RDONLY);
 
87
    check(fd >= 0, "Failed to open file but stat worked: %s", bdata(fr->full_path));
 
88
 
 
89
    fr->file_size = lseek(fd, 0L, SEEK_END);
 
90
    check(fr->file_size >= 0, "Failed to seek end of file: %s", bdata(fr->full_path));
 
91
    lseek(fd, 0L, SEEK_SET);
 
92
 
 
93
    fdclose(fd);
 
94
 
 
95
    return 0;
 
96
error:
 
97
 
 
98
    fdclose(fd);
 
99
    return -1;
 
100
}
 
101
 
 
102
 
83
103
 
84
104
FileRecord *Dir_find_file(bstring path, bstring default_type)
85
105
{
86
106
    FileRecord *fr = calloc(sizeof(FileRecord), 1);
87
 
    const char *p = bdata(path);
88
107
 
89
108
    check_mem(fr);
90
109
 
91
110
    // We set the number of users here.  If we cache it, we can add one later
92
111
    fr->users = 1;
 
112
    fr->full_path = path;
93
113
 
94
 
    int rc = stat(p, &fr->sb);
95
 
    check(rc == 0, "File stat failed: %s", bdata(path));
 
114
    int rc = stat(bdata(fr->full_path), &fr->sb);
 
115
    check(rc == 0, "File stat failed: %s", bdata(fr->full_path));
96
116
 
97
117
    if(S_ISDIR(fr->sb.st_mode)) {
98
 
        fr->full_path = path;
99
118
        fr->is_dir = 1;
100
119
        return fr;
101
120
    }
102
121
 
103
 
    fr->fd = open(p, O_RDONLY);
104
 
    check(fr->fd >= 0, "Failed to open file but stat worked: %s", bdata(path));
105
 
 
106
 
    fr->file_size = lseek(fr->fd, 0L, SEEK_END);
107
 
    lseek(fr->fd, 0L, SEEK_SET);
108
 
 
 
122
    check(get_file_real_size(fr) == 0, "Failed to setup the file record for %s", bdata(fr->full_path));
109
123
    fr->loaded = time(NULL);
110
124
 
111
125
    fr->last_mod = bStrfTime(RFC_822_TIME, gmtime(&fr->sb.st_mtime));
148
162
long long int Dir_stream_file(FileRecord *file, Connection *conn)
149
163
{
150
164
    long long int sent = 0;
 
165
    int fd = -1;
151
166
 
152
167
    int rc = Dir_send_header(file, conn);
153
168
    check_debug(rc, "Failed to write header to socket.");
154
169
 
155
 
    sent = IOBuf_stream_file(conn->iob, file->fd, file->file_size);
156
 
 
 
170
    fd = open(bdata(file->full_path), O_RDONLY);
 
171
    check(fd >= 0, "Failed to open file: %s", bdata(file->full_path));
 
172
 
 
173
    sent = IOBuf_stream_file(conn->iob, fd, file->file_size);
 
174
 
 
175
    fdclose(fd);
157
176
    return file->file_size;
158
177
 
159
178
error:
 
179
    if(fd >= 0) fdclose(fd);
160
180
    return -1;
161
181
}
162
182
 
229
249
{
230
250
    if(file) {
231
251
        if(!file->is_dir) {
232
 
            fdclose(file->fd);
233
252
            bdestroy(file->date);
234
253
            bdestroy(file->last_mod);
235
254
            bdestroy(file->header);
236
255
            bdestroy(file->etag);
 
256
            bdestroy(file->request_path);
237
257
        }
238
258
        bdestroy(file->full_path);
239
259
        // file->content_type is not owned by us
241
261
    }
242
262
}
243
263
 
 
264
static inline char *url_decode(const char *in, char *out)
 
265
{
 
266
  const char *cur; /* will seek % in input */
 
267
  char d1; /* will contain candidate for 1st digit */
 
268
  char d2; /* will contain candidate for 2nd digit */
 
269
  char *res = out; /* just for convienience */
 
270
 
 
271
  if(!in) {
 
272
    *out = '\0';
 
273
    return res;
 
274
  }
 
275
 
 
276
  cur = in;
 
277
 
 
278
  while(*cur) {
 
279
    d1 = *(cur+1);
 
280
    d2 = *(cur+2);
 
281
 
 
282
    /* One character left in input */
 
283
    if(!d1) {
 
284
      *out = *cur;
 
285
      *(out+1) = '\0';
 
286
      return res;
 
287
    }
 
288
 
 
289
    /* Two characters left in input */
 
290
    if(!d2) {
 
291
      *out = *cur;
 
292
      *(out+1) = *(cur+1);
 
293
      *(out+2) = '\0';
 
294
      return res;
 
295
    }
 
296
 
 
297
    /* Legal escape sequence */
 
298
    if(*cur=='%' && isxdigit(d1) && isxdigit(d2)) {
 
299
      d1 = tolower(d1);
 
300
      d2 = tolower(d2);
 
301
 
 
302
      if( d1 <= '9' )
 
303
        d1 = d1 - '0';
 
304
      else
 
305
        d1 = d1 - 'a' + 10;
 
306
      if( d2 <= '9' )
 
307
        d2 = d2 - '0';
 
308
      else
 
309
        d2 = d2 - 'a' + 10;
 
310
 
 
311
      *out = 16 * d1 + d2;
 
312
 
 
313
      out += 1;
 
314
      cur += 3;
 
315
    }
 
316
    else {
 
317
      *out = *cur;
 
318
      out += 1;
 
319
      cur += 1;
 
320
    }
 
321
  }
 
322
 
 
323
  *out = '\0';
 
324
  return res;
 
325
}
244
326
 
245
327
static inline int normalize_path(bstring target)
246
328
{
253
335
        check_mem(path_buf);
254
336
    }
255
337
 
 
338
    url_decode((const char *)(bdata(target)), path_buf);
 
339
    bassigncstr(target, path_buf);
 
340
 
256
341
    char *normalized = realpath((const char *)(bdata(target)), path_buf);
257
342
    check_debug(normalized, "Failed to normalize path: %s", bdata(target));
258
343
 
291
376
        if(difftime(now, file->loaded) > dir->cache_ttl) {
292
377
            int rcstat = stat(p, &sb);
293
378
 
294
 
            if(rcstat != 0 || file->sb.st_mtime != sb.st_mtime || file->sb.st_size != sb.st_size) {
 
379
            if(rcstat != 0 ||
 
380
                    file->sb.st_mtime != sb.st_mtime ||
 
381
                    file->sb.st_ctime != sb.st_ctime ||
 
382
                    file->sb.st_uid != sb.st_uid ||
 
383
                    file->sb.st_gid != sb.st_gid ||
 
384
                    file->sb.st_mode != sb.st_mode ||
 
385
                    file->sb.st_size != sb.st_size ||
 
386
                    file->sb.st_ino != sb.st_ino ||
 
387
                    file->sb.st_dev != sb.st_dev 
 
388
            ) {
295
389
                Cache_evict_object(dir->fr_cache, file);
296
390
                file = NULL;
297
391
            } else {
309
403
    FileRecord *file = NULL;
310
404
    bstring target = NULL;
311
405
 
 
406
    check(blength(prefix) <= blength(path), 
 
407
            "Path '%s' is shorter than prefix '%s', not allowed.", bdata(path), bdata(prefix));
 
408
 
312
409
    check(Dir_lazy_normalize_base(dir) == 0, "Failed to normalize base path when requesting %s",
313
410
            bdata(path));
314
411
 
337
434
                         bdata(dir->index_file));
338
435
    } else if(biseq(prefix, path)) {
339
436
        target = bformat("%s%s", bdata(dir->normalized_base), bdata(path));
340
 
 
341
437
    } else {
342
438
        target = bformat("%s/%s", bdata(dir->normalized_base), bdataofs(path, blength(prefix)));
343
439
    }
473
569
        rc = Response_send_status(conn, &HTTP_405);
474
570
        check_debug(rc == blength(&HTTP_405), "Failed to send 405 to client.");
475
571
        return -1;
 
572
    } else if (blength(prefix) > blength(path)) {
 
573
        req->status_code = 404;
 
574
        rc = Response_send_status(conn, &HTTP_404);
 
575
        check_debug(rc == blength(&HTTP_404), "Failed to send 404 to client.");
 
576
        return -1;
476
577
    } else {
477
578
        file = Dir_resolve_file(dir, prefix, path);
478
579
        resp = Dir_calculate_response(req, file);