00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <ctype.h>
00010 #include <assert.h>
00011 #include <stdlib.h>
00012 #include <stdio.h>
00013 #include <string.h>
00014 #include <errno.h>
00015 #include <time.h>
00016
00017 #include <xmlrpc-c/config.h>
00018 #include "abyss_mallocvar.h"
00019 #include "abyss_xmlrpc_int.h"
00020 #include <xmlrpc-c/abyss.h>
00021
00022 #include "abyss_server.h"
00023 #include "abyss_session.h"
00024 #include "abyss_conn.h"
00025 #include "abyss_token.h"
00026 #include "abyss_date.h"
00027 #include "abyss_data.h"
00028 #include "abyss_info.h"
00029 #include "abyss_http.h"
00030
00031
00032
00033 void
00034 ResponseError(TSession * const sessionP) {
00035
00036 const char * const reason = HTTPReasonByStatus(sessionP->status);
00037 const char * errorDocument;
00038
00039 ResponseAddField(sessionP, "Content-type", "text/html");
00040
00041 ResponseWriteStart(sessionP);
00042
00043 xmlrpc_asprintf(&errorDocument,
00044 "<HTML><HEAD><TITLE>Error %d</TITLE></HEAD>"
00045 "<BODY><H1>Error %d</H1><P>%s</P>" SERVER_HTML_INFO
00046 "</BODY></HTML>",
00047 sessionP->status, sessionP->status, reason);
00048
00049 ConnWrite(sessionP->conn, errorDocument, strlen(errorDocument));
00050
00051 xmlrpc_strfree(errorDocument);
00052 }
00053
00054
00055
00056 abyss_bool
00057 ResponseChunked(TSession * const sessionP) {
00058
00059
00060
00061 assert(!sessionP->responseStarted);
00062
00063 sessionP->chunkedwrite =
00064 (sessionP->version.major > 1) ||
00065 (sessionP->version.major == 1 && (sessionP->version.minor >= 1));
00066
00067 sessionP->chunkedwritemode = TRUE;
00068
00069 return TRUE;
00070 }
00071
00072
00073
00074 void
00075 ResponseStatus(TSession * const sessionP,
00076 uint16_t const code) {
00077
00078 sessionP->status = code;
00079 }
00080
00081
00082
00083 uint16_t
00084 ResponseStatusFromErrno(int const errnoArg) {
00085
00086 uint16_t code;
00087
00088 switch (errnoArg) {
00089 case EACCES:
00090 code=403;
00091 break;
00092 case ENOENT:
00093 code=404;
00094 break;
00095 default:
00096 code=500;
00097 }
00098 return code;
00099 }
00100
00101
00102
00103 void
00104 ResponseStatusErrno(TSession * const sessionP) {
00105
00106 ResponseStatus(sessionP, ResponseStatusFromErrno(errno));
00107 }
00108
00109
00110
00111 abyss_bool
00112 ResponseAddField(TSession * const sessionP,
00113 const char * const name,
00114 const char * const value) {
00115
00116 return TableAdd(&sessionP->response_headers, name, value);
00117 }
00118
00119
00120
00121 static void
00122 addDateHeader(TSession * const sessionP) {
00123
00124 char dateValue[64];
00125 abyss_bool validDate;
00126
00127 validDate = DateToString(&sessionP->date, dateValue);
00128
00129 if (sessionP->status >= 200 && validDate)
00130 ResponseAddField(sessionP, "Date", dateValue);
00131 }
00132
00133
00134
00135 void
00136 ResponseWriteStart(TSession * const sessionP) {
00137
00138 struct _TServer * const srvP = ConnServer(sessionP->conn)->srvP;
00139
00140 unsigned int i;
00141
00142 assert(!sessionP->responseStarted);
00143
00144 if (sessionP->status == 0) {
00145
00146 sessionP->status = 500;
00147 }
00148
00149 sessionP->responseStarted = TRUE;
00150
00151 {
00152 const char * const reason = HTTPReasonByStatus(sessionP->status);
00153 const char * line;
00154 xmlrpc_asprintf(&line,"HTTP/1.1 %u %s\r\n", sessionP->status, reason);
00155 ConnWrite(sessionP->conn, line, strlen(line));
00156 xmlrpc_strfree(line);
00157 }
00158
00159 if (HTTPKeepalive(sessionP)) {
00160 const char * keepaliveValue;
00161
00162 ResponseAddField(sessionP, "Connection", "Keep-Alive");
00163
00164 xmlrpc_asprintf(&keepaliveValue, "timeout=%u, max=%u",
00165 srvP->keepalivetimeout, srvP->keepalivemaxconn);
00166
00167 ResponseAddField(sessionP, "Keep-Alive", keepaliveValue);
00168
00169 xmlrpc_strfree(keepaliveValue);
00170 } else
00171 ResponseAddField(sessionP, "Connection", "close");
00172
00173 if (sessionP->chunkedwrite && sessionP->chunkedwritemode)
00174 ResponseAddField(sessionP, "Transfer-Encoding", "chunked");
00175
00176 addDateHeader(sessionP);
00177
00178
00179 if (srvP->advertise)
00180 ResponseAddField(sessionP, "Server", SERVER_HVERSION);
00181
00182
00183 for (i = 0; i < sessionP->response_headers.size; ++i) {
00184 TTableItem * const ti = &sessionP->response_headers.item[i];
00185 const char * line;
00186 xmlrpc_asprintf(&line, "%s: %s\r\n", ti->name, ti->value);
00187 ConnWrite(sessionP->conn, line, strlen(line));
00188 xmlrpc_strfree(line);
00189 }
00190
00191 ConnWrite(sessionP->conn, "\r\n", 2);
00192 }
00193
00194
00195
00196 abyss_bool
00197 ResponseWriteBody(TSession * const sessionP,
00198 const char * const data,
00199 uint32_t const len) {
00200
00201 return HTTPWriteBodyChunk(sessionP, data, len);
00202 }
00203
00204
00205
00206 abyss_bool
00207 ResponseWriteEnd(TSession * const sessionP) {
00208
00209 return HTTPWriteEndChunk(sessionP);
00210 }
00211
00212
00213
00214 abyss_bool
00215 ResponseContentType(TSession * const serverP,
00216 const char * const type) {
00217
00218 return ResponseAddField(serverP, "Content-type", type);
00219 }
00220
00221
00222
00223 abyss_bool
00224 ResponseContentLength(TSession * const sessionP,
00225 uint64_t const len) {
00226 char contentLengthValue[32];
00227
00228 sprintf(contentLengthValue, "%llu", (long long unsigned int)len);
00229
00230 return ResponseAddField(sessionP, "Content-length", contentLengthValue);
00231 }
00232
00233
00234
00235
00236
00237
00238 struct MIMEType {
00239 TList typeList;
00240 TList extList;
00241 TPool pool;
00242 };
00243
00244
00245 static MIMEType * globalMimeTypeP = NULL;
00246
00247
00248
00249 MIMEType *
00250 MIMETypeCreate(void) {
00251
00252 MIMEType * MIMETypeP;
00253
00254 MALLOCVAR(MIMETypeP);
00255
00256 if (MIMETypeP) {
00257 ListInit(&MIMETypeP->typeList);
00258 ListInit(&MIMETypeP->extList);
00259 PoolCreate(&MIMETypeP->pool, 1024);
00260 }
00261 return MIMETypeP;
00262 }
00263
00264
00265
00266 void
00267 MIMETypeDestroy(MIMEType * const MIMETypeP) {
00268
00269 PoolFree(&MIMETypeP->pool);
00270 }
00271
00272
00273
00274 void
00275 MIMETypeInit(void) {
00276
00277 if (globalMimeTypeP != NULL)
00278 abort();
00279
00280 globalMimeTypeP = MIMETypeCreate();
00281 }
00282
00283
00284
00285 void
00286 MIMETypeTerm(void) {
00287
00288 if (globalMimeTypeP == NULL)
00289 abort();
00290
00291 MIMETypeDestroy(globalMimeTypeP);
00292
00293 globalMimeTypeP = NULL;
00294 }
00295
00296
00297
00298 static void
00299 mimeTypeAdd(MIMEType * const MIMETypeP,
00300 const char * const type,
00301 const char * const ext,
00302 abyss_bool * const successP) {
00303
00304 uint16_t index;
00305 void * mimeTypesItem;
00306 abyss_bool typeIsInList;
00307
00308 assert(MIMETypeP != NULL);
00309
00310 typeIsInList = ListFindString(&MIMETypeP->typeList, type, &index);
00311 if (typeIsInList)
00312 mimeTypesItem = MIMETypeP->typeList.item[index];
00313 else
00314 mimeTypesItem = (void*)PoolStrdup(&MIMETypeP->pool, type);
00315
00316 if (mimeTypesItem) {
00317 abyss_bool extIsInList;
00318 extIsInList = ListFindString(&MIMETypeP->extList, ext, &index);
00319 if (extIsInList) {
00320 MIMETypeP->typeList.item[index] = mimeTypesItem;
00321 *successP = TRUE;
00322 } else {
00323 void * extItem = (void*)PoolStrdup(&MIMETypeP->pool, ext);
00324 if (extItem) {
00325 abyss_bool addedToMimeTypes;
00326
00327 addedToMimeTypes =
00328 ListAdd(&MIMETypeP->typeList, mimeTypesItem);
00329 if (addedToMimeTypes) {
00330 abyss_bool addedToExt;
00331
00332 addedToExt = ListAdd(&MIMETypeP->extList, extItem);
00333 *successP = addedToExt;
00334 if (!*successP)
00335 ListRemove(&MIMETypeP->typeList);
00336 } else
00337 *successP = FALSE;
00338 if (!*successP)
00339 PoolReturn(&MIMETypeP->pool, extItem);
00340 } else
00341 *successP = FALSE;
00342 }
00343 } else
00344 *successP = FALSE;
00345 }
00346
00347
00348
00349
00350 abyss_bool
00351 MIMETypeAdd2(MIMEType * const MIMETypeArg,
00352 const char * const type,
00353 const char * const ext) {
00354
00355 MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP;
00356
00357 abyss_bool success;
00358
00359 if (MIMETypeP == NULL)
00360 success = FALSE;
00361 else
00362 mimeTypeAdd(MIMETypeP, type, ext, &success);
00363
00364 return success;
00365 }
00366
00367
00368
00369 abyss_bool
00370 MIMETypeAdd(const char * const type,
00371 const char * const ext) {
00372
00373 return MIMETypeAdd2(globalMimeTypeP, type, ext);
00374 }
00375
00376
00377
00378 static const char *
00379 mimeTypeFromExt(MIMEType * const MIMETypeP,
00380 const char * const ext) {
00381
00382 const char * retval;
00383 uint16_t extindex;
00384 abyss_bool extIsInList;
00385
00386 assert(MIMETypeP != NULL);
00387
00388 extIsInList = ListFindString(&MIMETypeP->extList, ext, &extindex);
00389 if (!extIsInList)
00390 retval = NULL;
00391 else
00392 retval = MIMETypeP->typeList.item[extindex];
00393
00394 return retval;
00395 }
00396
00397
00398
00399 const char *
00400 MIMETypeFromExt2(MIMEType * const MIMETypeArg,
00401 const char * const ext) {
00402
00403 const char * retval;
00404
00405 MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP;
00406
00407 if (MIMETypeP == NULL)
00408 retval = NULL;
00409 else
00410 retval = mimeTypeFromExt(MIMETypeP, ext);
00411
00412 return retval;
00413 }
00414
00415
00416
00417 const char *
00418 MIMETypeFromExt(const char * const ext) {
00419
00420 return MIMETypeFromExt2(globalMimeTypeP, ext);
00421 }
00422
00423
00424
00425 static void
00426 findExtension(const char * const fileName,
00427 const char ** const extP) {
00428
00429 unsigned int extPos = 0;
00430
00431 abyss_bool extFound;
00432 unsigned int i;
00433
00434
00435 for (i = 0, extFound = FALSE; fileName[i]; ++i) {
00436 char const c = fileName[i];
00437
00438 if (c == '.') {
00439 extFound = TRUE;
00440 extPos = i + 1;
00441 }
00442 if (c == '/')
00443 extFound = FALSE;
00444 }
00445
00446 if (extFound)
00447 *extP = &fileName[extPos];
00448 else
00449 *extP = NULL;
00450 }
00451
00452
00453
00454 static const char *
00455 mimeTypeFromFileName(MIMEType * const MIMETypeP,
00456 const char * const fileName) {
00457
00458 const char * retval;
00459 const char * ext;
00460
00461 assert(MIMETypeP != NULL);
00462
00463 findExtension(fileName, &ext);
00464
00465 if (ext)
00466 retval = MIMETypeFromExt2(MIMETypeP, ext);
00467 else
00468 retval = "application/octet-stream";
00469
00470 return retval;
00471 }
00472
00473
00474
00475 const char *
00476 MIMETypeFromFileName2(MIMEType * const MIMETypeArg,
00477 const char * const fileName) {
00478
00479 const char * retval;
00480
00481 MIMEType * MIMETypeP = MIMETypeArg ? MIMETypeArg : globalMimeTypeP;
00482
00483 if (MIMETypeP == NULL)
00484 retval = NULL;
00485 else
00486 retval = mimeTypeFromFileName(MIMETypeP, fileName);
00487
00488 return retval;
00489 }
00490
00491
00492
00493 const char *
00494 MIMETypeFromFileName(const char * const fileName) {
00495
00496 return MIMETypeFromFileName2(globalMimeTypeP, fileName);
00497 }
00498
00499
00500
00501 static abyss_bool
00502 fileContainsText(const char * const fileName) {
00503
00504
00505
00506
00507 abyss_bool retval;
00508 abyss_bool fileOpened;
00509 TFile file;
00510
00511 fileOpened = FileOpen(&file, fileName, O_BINARY | O_RDONLY);
00512 if (fileOpened) {
00513 char const ctlZ = 26;
00514 unsigned char buffer[80];
00515 int32_t readRc;
00516 unsigned int i;
00517
00518 readRc = FileRead(&file, buffer, sizeof(buffer));
00519
00520 if (readRc >= 0) {
00521 unsigned int bytesRead = readRc;
00522 abyss_bool nonTextFound;
00523
00524 nonTextFound = FALSE;
00525
00526 for (i = 0; i < bytesRead; ++i) {
00527 char const c = buffer[i];
00528 if (c < ' ' && !isspace(c) && c != ctlZ)
00529 nonTextFound = TRUE;
00530 }
00531 retval = !nonTextFound;
00532 } else
00533 retval = FALSE;
00534 FileClose(&file);
00535 } else
00536 retval = FALSE;
00537
00538 return retval;
00539 }
00540
00541
00542
00543 static const char *
00544 mimeTypeGuessFromFile(MIMEType * const MIMETypeP,
00545 const char * const fileName) {
00546
00547 const char * retval;
00548 const char * ext;
00549
00550 findExtension(fileName, &ext);
00551
00552 retval = NULL;
00553
00554 if (ext && MIMETypeP)
00555 retval = MIMETypeFromExt2(MIMETypeP, ext);
00556
00557 if (!retval) {
00558 if (fileContainsText(fileName))
00559 retval = "text/plain";
00560 else
00561 retval = "application/octet-stream";
00562 }
00563 return retval;
00564 }
00565
00566
00567
00568 const char *
00569 MIMETypeGuessFromFile2(MIMEType * const MIMETypeArg,
00570 const char * const fileName) {
00571
00572 return mimeTypeGuessFromFile(MIMETypeArg ? MIMETypeArg : globalMimeTypeP,
00573 fileName);
00574 }
00575
00576
00577
00578 const char *
00579 MIMETypeGuessFromFile(const char * const fileName) {
00580
00581 return mimeTypeGuessFromFile(globalMimeTypeP, fileName);
00582 }
00583
00584
00585
00586
00587
00588
00589
00590 void Base64Encode(char *s,char *d)
00591 {
00592
00593 static char tbl[64] = {
00594 'A','B','C','D','E','F','G','H',
00595 'I','J','K','L','M','N','O','P',
00596 'Q','R','S','T','U','V','W','X',
00597 'Y','Z','a','b','c','d','e','f',
00598 'g','h','i','j','k','l','m','n',
00599 'o','p','q','r','s','t','u','v',
00600 'w','x','y','z','0','1','2','3',
00601 '4','5','6','7','8','9','+','/'
00602 };
00603
00604 uint32_t i,length=strlen(s);
00605 char *p=d;
00606
00607
00608 for (i = 0; i < length; i += 3)
00609 {
00610 *p++ = tbl[s[0] >> 2];
00611 *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
00612 *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
00613 *p++ = tbl[s[2] & 0x3f];
00614 s += 3;
00615 }
00616
00617
00618 if (i == length + 1)
00619 *(p - 1) = '=';
00620 else if (i == length + 2)
00621 *(p - 1) = *(p - 2) = '=';
00622
00623
00624 *p = '\0';
00625 }
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657