about summary refs log tree commit diff
path: root/sternenblog/cgiutil.c
blob: 7dc81145590da2eac51a4cc319f1392f8c970e61 (plain) (blame)
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
123
124
125
126
127
128
129
130
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stringutil.h"

void send_header(char key[], char val[]) {
    fputs(key, stdout);
    fputs(": ", stdout);
    fputs(val, stdout);
    puts("\r");
}

void terminate_headers(void) {
    puts("\r");
}

char *http_status_line(int status) {
    switch(status) {
        case 200:
            return "200 OK";
        case 400:
            return "400 Bad Request";
        case 401:
            return "401 Unauthorized";
        case 403:
            return "403 Forbidden";
        case 404:
            return "404 Not Found";
        default:
            // default to 500
            return "500 Internal Server Error";
    }
}

int http_errno(int err) {
    switch(err) {
        case EACCES:
            return 403;
        case ENOENT:
            return 404;
        case ENOTDIR:
            return 404;
        default:
            return 500;
    }
}

int urlencode_realloc(char **input, int size) {
    if(*input == NULL || size <= 0) {
        return -1;
    }

    int output_size = size;
    char *output = malloc(output_size);
    int output_pos = 0;

    if(output == NULL) {
        return -1;
    }

    for(int i = 0; i < size; i++) {
        char c = *(*input + i);
        bool needs_escape;
        switch(c) {
            // generic delimiters
            // we assume we never need to escape '/'. This
            // should hold since on unix filenames won't
            // contain slashes and the basis for all URLs
            // in sternenblog are actual files
            case ':': case '?': case '#': case '[': case ']': case '@':
            // sub delimiters
            case '!': case '$': case '&': case '\'': case '(': case ')':
            case '*': case '+': case ',': case ';': case '=':
            // other characters to encode
            case '%': case ' ':
                needs_escape = 1;
                break;
            // in order to simplify the code we just assume
            // everything else doesn't have to be encoded
            //
            // otherwise we'd need to be UTF-8 aware here
            // and consider more than one byte at a time.
            default:
                needs_escape = 0;
        }

        int necessary_space = needs_escape ? 3 : 1;

        if(output_pos + necessary_space >= output_size) {
            output_size += necessary_space;
            char *tmp = realloc(output, output_size);
            if(tmp == NULL) {
                free(output);
                return -1;
            } else {
                output = tmp;
            }
        }

        if(needs_escape) {
            short a = (c & 0xf0) >> 4;
            short b = c & 0x0f;
            output[output_pos++] = '%';
            output[output_pos++] = nibble_hex(a);
            output[output_pos++] = nibble_hex(b);
        } else {
            output[output_pos++] = c;
        }
    }

    free(*input);
    *input = output;

    return output_size;
}

char *server_url(bool https) {
    char *server_name = getenv("SERVER_NAME");
    char *server_port = getenv("SERVER_PORT");

    if(server_name == NULL || server_port == NULL) {
        return NULL;
    }

    char *proto = https ? "https://" : "http://";

    return catn_alloc(4, proto, server_name, ":", server_port);
}