1 /*=============================================================================
3 ===============================================================================
4 This module contains callbacks from and services for a request handler.
6 Copyright information is at the end of the file
7 =============================================================================*/
17 #include "xmlrpc_config.h"
18 #include "mallocvar.h"
19 #include "xmlrpc-c/string_int.h"
20 #include "xmlrpc-c/abyss.h"
28 #include "abyss_info.h"
34 ResponseError(TSession * const sessionP) {
36 const char * const reason = HTTPReasonByStatus(sessionP->status);
37 const char * errorDocument;
39 ResponseAddField(sessionP, "Content-type", "text/html");
41 ResponseWriteStart(sessionP);
43 xmlrpc_asprintf(&errorDocument,
44 "<HTML><HEAD><TITLE>Error %d</TITLE></HEAD>"
45 "<BODY><H1>Error %d</H1><P>%s</P>" SERVER_HTML_INFO
47 sessionP->status, sessionP->status, reason);
49 ConnWrite(sessionP->conn, errorDocument, strlen(errorDocument));
51 xmlrpc_strfree(errorDocument);
57 ResponseChunked(TSession * const sessionP) {
58 /* This is only a hope, things will be real only after a call of
61 assert(!sessionP->responseStarted);
63 sessionP->chunkedwrite =
64 (sessionP->version.major > 1) ||
65 (sessionP->version.major == 1 && (sessionP->version.minor >= 1));
67 sessionP->chunkedwritemode = TRUE;
75 ResponseStatus(TSession * const sessionP,
76 uint16_t const code) {
78 sessionP->status = code;
84 ResponseStatusFromErrno(int const errnoArg) {
104 ResponseStatusErrno(TSession * const sessionP) {
106 ResponseStatus(sessionP, ResponseStatusFromErrno(errno));
112 ResponseAddField(TSession * const sessionP,
113 const char * const name,
114 const char * const value) {
116 return TableAdd(&sessionP->response_headers, name, value);
122 addDateHeader(TSession * const sessionP) {
125 abyss_bool validDate;
127 validDate = DateToString(&sessionP->date, dateValue);
129 if (sessionP->status >= 200 && validDate)
130 ResponseAddField(sessionP, "Date", dateValue);
136 ResponseWriteStart(TSession * const sessionP) {
138 struct _TServer * const srvP = ConnServer(sessionP->conn)->srvP;
142 assert(!sessionP->responseStarted);
144 if (sessionP->status == 0) {
145 // Handler hasn't set status. That's an error
146 sessionP->status = 500;
149 sessionP->responseStarted = TRUE;
152 const char * const reason = HTTPReasonByStatus(sessionP->status);
154 xmlrpc_asprintf(&line,"HTTP/1.1 %u %s\r\n", sessionP->status, reason);
155 ConnWrite(sessionP->conn, line, strlen(line));
156 xmlrpc_strfree(line);
159 if (HTTPKeepalive(sessionP)) {
160 const char * keepaliveValue;
162 ResponseAddField(sessionP, "Connection", "Keep-Alive");
164 xmlrpc_asprintf(&keepaliveValue, "timeout=%u, max=%u",
165 srvP->keepalivetimeout, srvP->keepalivemaxconn);
167 ResponseAddField(sessionP, "Keep-Alive", keepaliveValue);
169 xmlrpc_strfree(keepaliveValue);
171 ResponseAddField(sessionP, "Connection", "close");
173 if (sessionP->chunkedwrite && sessionP->chunkedwritemode)
174 ResponseAddField(sessionP, "Transfer-Encoding", "chunked");
176 addDateHeader(sessionP);
178 /* Generation of the server field */
180 ResponseAddField(sessionP, "Server", SERVER_HVERSION);
182 /* send all the fields */
183 for (i = 0; i < sessionP->response_headers.size; ++i) {
184 TTableItem * const ti = &sessionP->response_headers.item[i];
186 xmlrpc_asprintf(&line, "%s: %s\r\n", ti->name, ti->value);
187 ConnWrite(sessionP->conn, line, strlen(line));
188 xmlrpc_strfree(line);
191 ConnWrite(sessionP->conn, "\r\n", 2);
197 ResponseWriteBody(TSession * const sessionP,
198 const char * const data,
199 uint32_t const len) {
201 return HTTPWriteBodyChunk(sessionP, data, len);
207 ResponseWriteEnd(TSession * const sessionP) {
209 return HTTPWriteEndChunk(sessionP);
215 ResponseContentType(TSession * const serverP,
216 const char * const type) {
218 return ResponseAddField(serverP, "Content-type", type);
224 ResponseContentLength(TSession * const sessionP,
225 uint64_t const len) {
226 char contentLengthValue[32];
228 sprintf(contentLengthValue, "%llu", len);
230 return ResponseAddField(sessionP, "Content-length", contentLengthValue);
234 /*********************************************************************
236 *********************************************************************/
245 static MIMEType * globalMimeTypeP = NULL;
250 MIMETypeCreate(void) {
252 MIMEType * MIMETypeP;
254 MALLOCVAR(MIMETypeP);
257 ListInit(&MIMETypeP->typeList);
258 ListInit(&MIMETypeP->extList);
259 PoolCreate(&MIMETypeP->pool, 1024);
267 MIMETypeDestroy(MIMEType * const MIMETypeP) {
269 PoolFree(&MIMETypeP->pool);
277 if (globalMimeTypeP != NULL)
280 globalMimeTypeP = MIMETypeCreate();
288 if (globalMimeTypeP == NULL)
291 MIMETypeDestroy(globalMimeTypeP);
293 globalMimeTypeP = NULL;
299 mimeTypeAdd(MIMEType * const MIMETypeP,
300 const char * const type,
301 const char * const ext,
302 abyss_bool * const successP) {
305 void * mimeTypesItem;
306 abyss_bool typeIsInList;
308 assert(MIMETypeP != NULL);
310 typeIsInList = ListFindString(&MIMETypeP->typeList, type, &index);
312 mimeTypesItem = MIMETypeP->typeList.item[index];
314 mimeTypesItem = (void*)PoolStrdup(&MIMETypeP->pool, type);
317 abyss_bool extIsInList;
318 extIsInList = ListFindString(&MIMETypeP->extList, ext, &index);
320 MIMETypeP->typeList.item[index] = mimeTypesItem;
323 void * extItem = (void*)PoolStrdup(&MIMETypeP->pool, ext);
325 abyss_bool addedToMimeTypes;
328 ListAdd(&MIMETypeP->typeList, mimeTypesItem);
329 if (addedToMimeTypes) {
330 abyss_bool addedToExt;
332 addedToExt = ListAdd(&MIMETypeP->extList, extItem);
333 *successP = addedToExt;
335 ListRemove(&MIMETypeP->typeList);
339 PoolReturn(&MIMETypeP->pool, extItem);
351 MIMETypeAdd2(MIMEType * const MIMETypeArg,
352 const char * const type,
353 const char * const ext) {
355 MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP;
359 if (MIMETypeP == NULL)
362 mimeTypeAdd(MIMETypeP, type, ext, &success);
370 MIMETypeAdd(const char * const type,
371 const char * const ext) {
373 return MIMETypeAdd2(globalMimeTypeP, type, ext);
379 mimeTypeFromExt(MIMEType * const MIMETypeP,
380 const char * const ext) {
384 abyss_bool extIsInList;
386 assert(MIMETypeP != NULL);
388 extIsInList = ListFindString(&MIMETypeP->extList, ext, &extindex);
392 retval = MIMETypeP->typeList.item[extindex];
400 MIMETypeFromExt2(MIMEType * const MIMETypeArg,
401 const char * const ext) {
405 MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP;
407 if (MIMETypeP == NULL)
410 retval = mimeTypeFromExt(MIMETypeP, ext);
418 MIMETypeFromExt(const char * const ext) {
420 return MIMETypeFromExt2(globalMimeTypeP, ext);
426 findExtension(const char * const fileName,
427 const char ** const extP) {
429 unsigned int extPos = 0; /* stifle unset variable warning */
430 /* Running estimation of where in fileName[] the extension starts */
434 /* We're looking for the last dot after the last slash */
435 for (i = 0, extFound = FALSE; fileName[i]; ++i) {
436 char const c = fileName[i];
447 *extP = &fileName[extPos];
455 mimeTypeFromFileName(MIMEType * const MIMETypeP,
456 const char * const fileName) {
461 assert(MIMETypeP != NULL);
463 findExtension(fileName, &ext);
466 retval = MIMETypeFromExt2(MIMETypeP, ext);
468 retval = "application/octet-stream";
476 MIMETypeFromFileName2(MIMEType * const MIMETypeArg,
477 const char * const fileName) {
481 MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP;
483 if (MIMETypeP == NULL)
486 retval = mimeTypeFromFileName(MIMETypeP, fileName);
494 MIMETypeFromFileName(const char * const fileName) {
496 return MIMETypeFromFileName2(globalMimeTypeP, fileName);
502 fileContainsText(const char * const fileName) {
503 /*----------------------------------------------------------------------------
504 Return true iff we can read the contents of the file named 'fileName'
505 and see that it appears to be composed of plain text characters.
506 -----------------------------------------------------------------------------*/
508 abyss_bool fileOpened;
511 fileOpened = FileOpen(&file, fileName, O_BINARY | O_RDONLY);
513 char const ctlZ = 26;
514 unsigned char buffer[80];
518 readRc = FileRead(&file, buffer, sizeof(buffer));
521 unsigned int bytesRead = readRc;
522 abyss_bool nonTextFound;
524 nonTextFound = FALSE; /* initial value */
526 for (i = 0; i < bytesRead; ++i) {
527 char const c = buffer[i];
528 if (c < ' ' && !isspace(c) && c != ctlZ)
531 retval = !nonTextFound;
544 mimeTypeGuessFromFile(MIMEType * const MIMETypeP,
545 const char * const fileName) {
550 findExtension(fileName, &ext);
554 if (ext && MIMETypeP)
555 retval = MIMETypeFromExt2(MIMETypeP, ext);
558 if (fileContainsText(fileName))
559 retval = "text/plain";
561 retval = "application/octet-stream";
569 MIMETypeGuessFromFile2(MIMEType * const MIMETypeArg,
570 const char * const fileName) {
572 return mimeTypeGuessFromFile(MIMETypeArg ? MIMETypeArg : globalMimeTypeP,
579 MIMETypeGuessFromFile(const char * const fileName) {
581 return mimeTypeGuessFromFile(globalMimeTypeP, fileName);
586 /*********************************************************************
588 *********************************************************************/
590 void Base64Encode(char *s,char *d)
592 /* Conversion table. */
593 static char tbl[64] = {
594 'A','B','C','D','E','F','G','H',
595 'I','J','K','L','M','N','O','P',
596 'Q','R','S','T','U','V','W','X',
597 'Y','Z','a','b','c','d','e','f',
598 'g','h','i','j','k','l','m','n',
599 'o','p','q','r','s','t','u','v',
600 'w','x','y','z','0','1','2','3',
601 '4','5','6','7','8','9','+','/'
604 uint32_t i,length=strlen(s);
607 /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
608 for (i = 0; i < length; i += 3)
610 *p++ = tbl[s[0] >> 2];
611 *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
612 *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
613 *p++ = tbl[s[2] & 0x3f];
617 /* Pad the result if necessary... */
620 else if (i == length + 2)
621 *(p - 1) = *(p - 2) = '=';
623 /* ...and zero-terminate it. */
627 /******************************************************************************
631 ** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
632 ** All rights reserved.
634 ** Redistribution and use in source and binary forms, with or without
635 ** modification, are permitted provided that the following conditions
637 ** 1. Redistributions of source code must retain the above copyright
638 ** notice, this list of conditions and the following disclaimer.
639 ** 2. Redistributions in binary form must reproduce the above copyright
640 ** notice, this list of conditions and the following disclaimer in the
641 ** documentation and/or other materials provided with the distribution.
642 ** 3. The name of the author may not be used to endorse or promote products
643 ** derived from this software without specific prior written permission.
645 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
646 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
647 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
648 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
649 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
650 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
651 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
652 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
653 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
654 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
657 ******************************************************************************/