lots of moving, lots of tidy up, lots removed - also directory requests work again!
[krsd] / src / handler / storage.c
1 /*
2  * rs-serve - (c) 2013 Niklas E. Cathor
3  *
4  * This program is distributed in the hope that it will be useful,
5  * but WITHOUT ANY WARRANTY; without even the implied warranty of
6  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
7  * GNU Affero General Public License for more details.
8  *
9  * You should have received a copy of the GNU Affero General Public License
10  * along with this program. If not, see <http://www.gnu.org/licenses/>.
11  */
12
13 #include "rs-serve.h"
14
15 /*
16  * Storage Handler
17  * ---------------
18  *
19  * Gets parsed requests and performs the requested actions / sends the requested
20  * response.
21  *
22  * These functions are only called from storage processes.
23  */
24
25 static char *escape_name(const char *name) {
26   int max_len = strlen(name) * 2, i = 0;
27   char *escaped = malloc(max_len + 1);
28   if(escaped == NULL) {
29     perror("malloc() failed");
30     return NULL;
31   }
32   const char *name_p;
33   for(name_p = name; *name_p != 0; name_p++) {
34     if(*name_p == '"' || *name_p == '\\') {
35       escaped[i++] = '\\';
36     }
37     escaped[i++] = *name_p;
38   }
39   escaped[i++] = 0;
40   escaped = realloc(escaped, i);
41   return escaped;
42 }
43
44 static int serve_directory(struct rs_request *request) {
45   struct evbuffer *buf = evbuffer_new();
46   if(buf == NULL) {
47     log_error("evbuffer_new() failed: %s", strerror(errno));
48     return 500;
49   }
50   DIR *dir = opendir(request->path);
51   if(dir == NULL) {
52     log_error("opendir() failed: %s", strerror(errno));
53     return 500;
54   }
55   struct dirent *entryp = malloc(offsetof(struct dirent, d_name) +
56                                  pathconf(request->path, _PC_NAME_MAX) + 1);
57   struct dirent *resultp = NULL;
58   if(entryp == NULL) {
59     log_error("malloc() failed while creating directory pointer: %s",
60               strerror(errno));
61     return 500;
62   }
63   struct stat file_stat_buf;
64   int entry_len;
65   int first = 1;
66   for(;;) {
67     readdir_r(dir, entryp, &resultp);
68     if(resultp == NULL) {
69       break;
70     }
71     if(strcmp(entryp->d_name, ".") == 0 ||
72        strcmp(entryp->d_name, "..") == 0) {
73       // skip.
74       continue;
75     }
76     if(first) {
77       evbuffer_add(buf, "{", 1);
78       first = 0;
79     } else {
80       evbuffer_add(buf, ",", 1);
81     }
82     entry_len = strlen(entryp->d_name);
83     char full_path[request->path_len + entry_len + 1];
84     sprintf(full_path, "%s%s", request->path, entryp->d_name);
85     stat(full_path, &file_stat_buf);
86     
87     char *escaped_name = escape_name(entryp->d_name);
88     if(! escaped_name) {
89       // failed to allocate name
90       free(entryp);
91       free(dir);
92       return 500;
93     }
94     evbuffer_add_printf(buf, "\"%s%s\":%lld", escaped_name,
95                         S_ISDIR(file_stat_buf.st_mode) ? "/" : "",
96                         ((long long)file_stat_buf.st_mtime) * 1000);
97     free(escaped_name);
98   }
99   if(first) {
100     // empty directory.
101     evbuffer_add(buf, "{", 1);
102   }
103   evbuffer_add(buf, "}", 1);
104   struct rs_header header = {
105     .key = "Content-Type",
106     .value = "application/json",
107     .next = NULL
108   };
109   send_response_head(request, 200, &header);
110   send_response_body(request, buf);
111   free(entryp);
112   closedir(dir);
113   return 0;
114 }
115
116 int serve_file(struct rs_request *request) {
117   return 501;
118 }
119
120 int storage_handle_head(struct rs_request *request) {
121   return 501;
122 }
123
124 int storage_handle_get(struct rs_request *request) {
125   // stat
126   struct stat stat_buf;
127   if(stat(request->path, &stat_buf) != 0) {
128     if(errno != ENOENT) {
129       log_error("stat() failed for path \"%s\": %s", request->path, strerror(errno));
130       return 500;
131     } else {
132       return 404;
133     }
134   }
135   // check for directory
136   if(request->path[request->path_len - 1] == '/') {
137     // directory requested
138     if(! S_ISDIR(stat_buf.st_mode)) {
139       // not a directory.
140       return 404;
141     }
142     // directory found
143     return serve_directory(request);
144   } else {
145     // file requested
146     if(S_ISDIR(stat_buf.st_mode)) {
147       // found, but is a directory
148       return 404;
149     }
150     // file found
151     return serve_file(request);
152   }
153 }
154
155 int storage_handle_put(struct rs_request *request) {
156   return 501;
157 }
158
159 int storage_handle_delete(struct rs_request *request) {
160   return 501;
161 }