Experimental Palm Plugin (disabled by default)
[MonkeyD.git] / plugins / palm / palm.c
blobbb5d661686363bca15c8f907ecd6995dc39db3de
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /* Monkey HTTP Daemon
4 * ------------------
5 * Copyright (C) 2001-2010, Eduardo Silva P.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
28 #include "config.h"
29 #include "plugin.h"
30 #include "str.h"
31 #include "palm.h"
32 #include "http.h"
33 #include "http_status.h"
34 #include "monkey.h"
35 #include "epoll.h"
36 #include "utils.h"
37 #include "header.h"
39 #include "cgi.h"
40 #include "palm.h"
42 /* Plugin data for register */
43 mk_plugin_data_t _shortname = "palm";
44 mk_plugin_data_t _name = "Palm";
45 mk_plugin_data_t _version = "0.1";
46 mk_plugin_stage_t _stages = MK_PLUGIN_STAGE_10 | MK_PLUGIN_STAGE_40;
48 /* Read database configuration parameters */
49 int mk_palm_conf(char *confdir)
51 int i, ret = 0;
52 unsigned long len;
53 char *conf_path;
54 struct mk_config *p;
55 struct mk_palm *new, *r;
56 struct mk_string_line *line, *lp;
58 mk_api->str_build(&conf_path, &len, "%s/palm.conf", confdir);
60 p = conf = mk_api->config_create(conf_path);
62 r = palms;
63 while (p) {
64 /* Validate palm configuration line */
65 i = 0;
66 if (strcasecmp(p->key, "Palm") == 0) {
67 line = mk_api->str_split_line(p->val);
68 lp = line;
70 while (lp) {
71 i++;
72 if (lp->len <= 0) {
73 fprintf(stderr, MK_PALM_ERROR_LINE, p->val);
74 _exit(1);
76 lp = lp->next;
79 if (i != 4) {
80 fprintf(stderr, MK_PALM_ERROR_LINE, p->val);
81 _exit(1);
84 lp = line;
86 /* Alloc node */
87 new = mk_api->mem_alloc(sizeof(struct mk_palm));
89 /* Palm file extensions */
90 new->extension = lp->val;
91 lp = lp->next;
93 /* Palm mime type */
94 new->mimetype = lp->val;
95 lp = lp->next;
97 /* Palm server address */
98 new->server_addr = lp->val;
99 lp = lp->next;
101 /* Palm server TCP port */
102 new->server_port = atoi(lp->val);
103 lp = lp->next;
105 new->next = NULL;
107 /* Linking node */
108 if (!palms) {
109 palms = new;
111 else {
112 r = palms;
113 while (r->next) {
114 r = r->next;
116 r->next = new;
118 p = p->next;
121 mk_api->mem_free(conf_path);
122 return ret;
125 struct mk_palm *mk_palm_get_handler(mk_pointer * file)
127 struct mk_palm *p;
128 int j, len, extlen;
130 j = len = file->len;
132 /* looking for extension */
133 while (file->data[j] != '.' && j >= 0) {
134 j--;
137 extlen = file->len - j - 1;
138 if (j == 0) {
139 return NULL;
142 p = palms;
143 while (p) {
144 if (strncasecmp(file->data + j + 1, p->extension, extlen) == 0) {
145 return p;
147 p = p->next;
150 return NULL;
153 void mk_palm_iov_add_header(struct mk_iov *iov,
154 mk_pointer header, mk_pointer value)
156 mk_api->iov_add_entry(iov, header.data, header.len,
157 mk_iov_equal, MK_IOV_NOT_FREE_BUF);
158 mk_api->iov_add_entry(iov, value.data, value.len,
159 mk_iov_crlf, MK_IOV_NOT_FREE_BUF);
162 struct mk_iov *mk_palm_create_env(struct client_request *cr,
163 struct request *sr)
165 struct mk_iov *iov;
167 iov = mk_api->iov_create(100, 0);
169 mk_api->iov_add_entry(iov, sr->real_path.data,
170 sr->real_path.len, mk_iov_crlf, MK_IOV_NOT_FREE_BUF);
172 mk_api->iov_add_entry(iov, mk_cgi_document_root.data,
173 mk_cgi_document_root.len,
174 mk_iov_equal, MK_IOV_NOT_FREE_BUF);
176 mk_api->iov_add_entry(iov, sr->host_conf->documentroot.data,
177 sr->host_conf->documentroot.len, mk_iov_crlf,
178 MK_IOV_NOT_FREE_BUF);
181 if (sr->method == HTTP_METHOD_POST && sr->content_length > 0) {
182 /* FIX Content length:
183 mk_palm_iov_add_header(iov, mk_cgi_content_length,
184 sr->content_length);
186 mk_palm_iov_add_header(iov, mk_cgi_content_type, sr->content_type);
190 // mk_palm_iov_add_header(iov, mk_cgi_server_addr, mk_api->config->server_addr);
191 mk_palm_iov_add_header(iov, mk_cgi_server_name, sr->host);
192 mk_palm_iov_add_header(iov, mk_cgi_server_protocol, mk_monkey_protocol);
193 mk_palm_iov_add_header(iov, mk_cgi_server_software,
194 mk_api->config->server_software);
195 //mk_palm_iov_add_header(iov, mk_cgi_server_signature, sr->host_conf->host_signature);
197 if (sr->user_agent.data)
198 mk_palm_iov_add_header(iov, mk_cgi_http_user_agent, sr->user_agent);
200 if (sr->accept.data)
201 mk_palm_iov_add_header(iov, mk_cgi_http_accept, sr->accept);
203 if (sr->accept_charset.data)
204 mk_palm_iov_add_header(iov, mk_cgi_http_accept_charset,
205 sr->accept_charset);
207 if (sr->accept_encoding.data)
208 mk_palm_iov_add_header(iov, mk_cgi_http_accept_encoding,
209 sr->accept_encoding);
211 if (sr->accept_language.data)
212 mk_palm_iov_add_header(iov, mk_cgi_http_accept_language,
213 sr->accept_language);
215 if (sr->host.data)
216 mk_palm_iov_add_header(iov, mk_cgi_http_host, sr->host);
218 if (sr->cookies.data)
219 mk_palm_iov_add_header(iov, mk_cgi_http_cookie, sr->cookies);
221 if (sr->referer.data)
222 mk_palm_iov_add_header(iov, mk_cgi_http_referer, sr->referer);
224 // mk_palm_iov_add_header(iov, mk_cgi_server_port, mk_monkey_port);
225 mk_palm_iov_add_header(iov, mk_cgi_gateway_interface, mk_cgi_version);
226 //mk_palm_iov_add_header(iov, mk_cgi_remote_addr, cr->ip);
227 mk_palm_iov_add_header(iov, mk_cgi_request_uri, sr->uri);
228 //mk_palm_iov_add_header(iov, mk_cgi_request_method, sr->method);
229 mk_palm_iov_add_header(iov, mk_cgi_script_name, sr->uri);
232 /* real path is not an mk_pointer */
233 mk_palm_iov_add_header(iov, mk_cgi_script_filename, sr->real_path);
234 //mk_palm_iov_add_header(iov, mk_cgi_remote_port, cr->port);
235 mk_palm_iov_add_header(iov, mk_cgi_query_string, sr->query_string);
236 //mk_palm_iov_add_header(iov, mk_cgi_post_vars, sr->post_variables);
238 /* CRLF */
239 mk_api->iov_add_entry(iov, mk_iov_crlf.data, mk_iov_crlf.len,
240 mk_iov_none, MK_IOV_NOT_FREE_BUF);
241 mk_api->iov_add_entry(iov, mk_iov_crlf.data, mk_iov_crlf.len,
242 mk_iov_none, MK_IOV_NOT_FREE_BUF);
243 mk_api->iov_add_entry(iov, mk_iov_crlf.data, mk_iov_crlf.len,
244 mk_iov_none, MK_IOV_NOT_FREE_BUF);
245 return iov;
249 int mk_palm_send_response(struct client_request *cr, struct request *sr,
250 char *buf)
252 int len;
253 int i;
254 long n;
255 char *s;
256 char *status_msg = "Status: ";
258 len = 8;
259 if (strncasecmp(buf, status_msg, len) == 0) {
260 i = (int) mk_api->str_search(buf + len, " ");
261 s = mk_api->str_copy_substr(buf, len, len + i);
262 sr->headers->status = atoi(s);
263 i = (int) mk_api->str_search(buf, mk_crlf.data) + mk_crlf.len;
265 else {
266 i = 0;
267 sr->headers->status = M_HTTP_OK;
270 sr->headers->cgi = SH_CGI;
271 sr->headers->content_length = 0;
273 mk_api->socket_cork_flag(cr->socket, TCP_CORK_ON);
274 mk_api->header_send(cr->socket, cr, sr, sr->log);
275 n = write(cr->socket, buf + i, strlen(buf + i));
276 return 0;
280 int _mk_plugin_init(void **api, char *confdir)
282 mk_api = *api;
283 palms = 0;
285 /* Init some pointers */
286 mk_api->pointer_set(&mk_monkey_protocol, HTTP_PROTOCOL_11_STR);
287 mk_api->pointer_set(&mk_iov_crlf, MK_IOV_CRLF);
288 mk_api->pointer_set(&mk_iov_equal, MK_IOV_EQUAL);
290 /* Read configuration */
291 mk_palm_conf(confdir);
292 return 0;
295 int _mk_plugin_stage_10(struct server_config *config)
297 mk_cgi_env();
298 return 0;
301 int _mk_plugin_stage_40(struct plugin *plugin, struct client_request *cr, struct request *sr)
303 struct mk_palm *palm;
304 struct mk_palm_request *pr;
306 PLUGIN_TRACE("PALM STAGE 40");
307 PLUGIN_TRACE("real_path:'%s'", sr->real_path.data);
309 palm = mk_palm_get_handler(&sr->uri);
310 if (!palm) {
311 return MK_PLUGIN_RET_NOT_ME;
313 else {
314 /* Connect to server */
315 pr = mk_palm_do_instance(palm, cr, sr);
317 if (!pr) {
318 PLUGIN_TRACE("return %i (MK_PLUGIN_RET_END)", MK_PLUGIN_RET_END);
319 return MK_PLUGIN_RET_END;
322 /* Register Palm instance */
323 mk_palm_request_add(pr);
325 /* Register socket with thread Epoll interface */
326 mk_api->event_add(pr->palm_fd, plugin, cr, sr);
327 PLUGIN_TRACE("Palm: Event registered / client=%i / palm_socket=%i",
328 pr->client_fd, pr->palm_fd);
331 PLUGIN_TRACE("return %i (MK_PLUGIN_RET_CONTINUE)", MK_PLUGIN_RET_CONTINUE);
332 return MK_PLUGIN_RET_CONTINUE;
335 struct mk_palm_request *mk_palm_request_create(int client_fd,
336 int palm_fd,
337 struct client_request *cr,
338 struct request *sr,
339 struct mk_palm *palm)
341 struct mk_palm_request *new;
343 new = mk_api->mem_alloc(sizeof(struct mk_palm_request));
344 new->client_fd = client_fd;
345 new->palm_fd = palm_fd;
346 new->palm = palm;
347 new->bytes_sent = 0;
348 new->bytes_read = 0;
349 new->headers_sent = VAR_OFF;
350 new->cr = cr;
351 new->sr = sr;
352 new->next = NULL;
354 return new;
357 struct mk_palm_request *mk_palm_do_instance(struct mk_palm *palm,
358 struct client_request *cr, struct request *sr)
360 int ret;
361 int palm_socket;
363 /* Get Palm handler */
364 palm = mk_palm_get_handler(&sr->uri);
366 /* Connecting to Palm Server */
367 palm_socket = (int) mk_api->socket_create();
368 ret = (int) mk_api->socket_connect(palm_socket,
369 palm->server_addr,
370 palm->server_port);
372 if (ret < 0) {
373 fprintf(stderr, "\nPalm: Cannot connect to %s on port %i",
374 palm->server_addr, palm->server_port);
375 return NULL;
377 /* Set palm socket to non-blocking */
378 mk_api->socket_set_nonblocking(palm_socket);
380 /* Return instance */
381 return mk_palm_request_create(cr->socket, palm_socket, cr, sr, palm);
384 void mk_palm_request_add(struct mk_palm_request *pr)
386 struct mk_palm_request *pr_list, *aux;
388 /* Get thread data */
389 pr_list = pthread_getspecific(_mk_plugin_data);
391 /* No connection previously was found */
392 if(!pr_list) {
393 pthread_setspecific(_mk_plugin_data, pr);
394 return;
397 /* Add Node */
398 aux = pr_list;
399 while(aux->next){
400 aux = aux->next;
403 aux->next = pr;
404 pthread_setspecific(_mk_plugin_data, pr_list);
407 /* It register the request and connection data, if it doesn't
408 * exists it will be create it, otherwise will return the pointer
409 * to the mk_palm_request struct node
411 struct mk_palm_request *mk_palm_request_get(int socket)
413 struct mk_palm_request *pr, *aux;
415 /* Get thread data */
416 pr = pthread_getspecific(_mk_plugin_data);
418 /* No connection previously was found */
419 if(!pr) {
420 return NULL;
423 /* Look for node */
424 aux = pr;
425 while(aux){
426 if(aux->client_fd == socket){
427 return aux;
429 aux = aux->next;
432 return NULL;
435 void mk_palm_request_update(int socket, struct mk_palm_request *pr)
437 struct mk_palm_request *aux, *list;
439 list = pthread_getspecific(_mk_plugin_data);
441 if (!list) {
442 return;
445 aux = list;
446 while (aux) {
447 if (aux->client_fd == socket) {
448 aux->bytes_sent = pr->bytes_sent;
449 aux->bytes_read = pr->bytes_read;
450 aux->headers_sent = pr->headers_sent;
452 /* Update data */
453 pthread_setspecific(_mk_plugin_data, list);
454 return;
456 aux = aux->next;
460 void _mk_plugin_stage_40_event_write(struct client_request *cr, struct request *sr)
462 int n;
463 ssize_t bytes_iov=-1;
464 struct mk_iov *iov;
465 struct mk_palm_request *pr;
467 PLUGIN_TRACE("Handling write event");
469 pr = mk_palm_request_get(cr->socket);
470 if (pr) {
471 if (pr->bytes_sent == 0) {
473 PLUGIN_TRACE("Palm request: '%s'", sr->real_path.data);
475 /* Palm environment vars */
476 iov = mk_palm_create_env(cr, sr);
478 /* Setup Palm socket */
479 mk_api->socket_set_tcp_nodelay(pr->palm_fd);
480 /* Write request to palm server */
481 bytes_iov = (ssize_t )mk_api->iov_send(pr->palm_fd, iov, MK_IOV_SEND_TO_SOCKET);
483 if (bytes_iov >= 0){
484 pr->bytes_sent += bytes_iov;
485 n = (long) bytes_iov;
490 PLUGIN_TRACE("Read: %i Write: %i", pr->bytes_read, pr->bytes_sent);
491 mk_api->event_socket_change_mode(pr->palm_fd, MK_EPOLL_READ);
495 int _mk_plugin_stage_40_event_read(struct client_request *cr, struct request *sr)
497 int n;
498 int n_header;
499 int ret;
500 int headers_end=-1;
501 char *chunk_size=0;
502 unsigned long len;
503 struct mk_palm_request *pr;
505 pr = mk_palm_request_get(cr->socket);
507 if (!pr){
508 PLUGIN_TRACE("Invalid palm request, not found");
509 return -1;
512 PLUGIN_TRACE("Reading data from FD %i", pr->palm_fd);
514 /* Reset read buffer */
515 bzero(pr->data_read, MK_PALM_BUFFER_SIZE);
516 pr->n_read = 0;
518 /* Read data */
519 pr->n_read = read(pr->palm_fd, pr->data_read, (MK_PALM_BUFFER_SIZE - 1));
521 if (pr->n_read < 0) {
522 perror("read");
525 PLUGIN_TRACE("Bytes read %i", pr->n_read);
527 if (pr->n_read >=0) {
528 if (pr->headers_sent == VAR_OFF) {
529 PLUGIN_TRACE("Sending headers");
531 sr->headers->status = M_HTTP_OK;
532 sr->headers->cgi = SH_CGI;
534 /* Chunked transfer encoding */
535 if (sr->protocol >= HTTP_PROTOCOL_11) {
536 sr->headers->transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
539 /* Look for headers end */
540 headers_end = (int) mk_api->str_search(pr->buffer, MK_IOV_CRLFCRLF);
542 PLUGIN_TRACE("Headers end %i", headers_end);
544 if (headers_end > 0) {
545 headers_end += 4;
548 /* Send just headers from buffer */
549 n_header = (int) mk_api->header_send(cr->socket, cr, sr, sr->log);
550 write(cr->socket, pr->buffer, headers_end);
552 /* Send first chunk data */
553 mk_api->str_build(&chunk_size, &len, "%x%s", n, MK_CRLF);
554 int a;
555 a = write(cr->socket, chunk_size, len);
557 PLUGIN_TRACE("first write: %i bytes", a);
559 n = write(cr->socket, pr->buffer + headers_end, n - headers_end);
561 PLUGIN_TRACE("second write: %i bytes", n);
563 /* Turn off TCP_CORK_OFF */
564 mk_api->socket_cork_flag(cr->socket, TCP_CORK_OFF);
565 pr->bytes_sent_to_client = 1;
567 else {
568 pr->bytes_read += n;
570 PLUGIN_TRACE("Just N: %i", n);
571 PLUGIN_TRACE("Total read from palm server: %i", pr->bytes_read);
573 //n = send(0, pr->buffer, n, 0);
575 int l;
576 l = (int) mk_api->str_build(&chunk_size, &len,"%x%s", n, MK_CRLF);
577 PLUGIN_TRACE("chunk: %s (len: %i)", chunk_size, len);
579 write(cr->socket, chunk_size, l);
580 n = write(pr->client_fd, pr->buffer, n);
581 if (n < 0) {
582 perror("write");
585 PLUGIN_TRACE("Bytes sent to client %i: %i", pr->client_fd, n);
587 ret = MK_PLUGIN_RET_CONTINUE;
591 else {
592 PLUGIN_TRACE("BIG ERROR!");
595 write(cr->socket, MK_CRLF, 2);
597 mk_api->mem_free(chunk_size);
598 chunk_size = '\0';
600 /* Update thread node info */
601 mk_palm_request_update(cr->socket, pr);
603 return ret;