1 /*
2 mime.c
3 This file is part of pserv
4 http://pserv.sourceforge.net
5
6 Copyright (c) 2001-2002-2003-2004 Riccardo Mottola. All rights reserved.
7 mail: rmottola@users.sourceforge.net
8
9 This file is free software, released under GPL. Please read acclosed license
10 */
11
12 #include <ctype.h> /* tolower char conversion */
13
14 #include "mime.h"
15 #include "handlers.h" /* for sendChunk routine */
16 #include "log.h"
17 #include "main.h"
18
19 extern char mimeTypesFileName[MAX_PATH_LEN+1];
20 extern mimeData *mimeArray;
21 extern int mimeEntries;
22
lowerCase(str)23 int lowerCase(str)
24 /* converts the given string to lowercase, by modifying it */
25 /* should need some not-terminatesdstring protection ? */
26 char *str;
27 {
28 int i;
29
30 i = 0;
31 while (str[i] != '\0')
32 {
33 str[i] = tolower(str[i]);
34 i++;
35 }
36 return 0;
37 }
38
initMimeTypes()39 int initMimeTypes()
40 /* load mime types from file */
41 {
42 FILE *f;
43 int entries;
44 int i;
45 char str[BUFFER_SIZE];
46
47 /* first we check how many entries we have */
48 f = fopen(mimeTypesFileName, "r");
49 if (f == NULL)
50 {
51 printf("Mime types file not found. Setting defaults.\n");
52 entries = 6;
53 mimeArray = (mimeData *) calloc(entries, sizeof(mimeData));
54 if (mimeArray == NULL) {
55 printf("Errory while allocating mime types Array. Exiting.\n");
56 return -2;
57 }
58 strcpy(mimeArray[0].ext, "html");
59 strcpy(mimeArray[0].type, "text/html");
60 strcpy(mimeArray[1].ext, "htm");
61 strcpy(mimeArray[1].type, "text/html");
62 strcpy(mimeArray[2].ext, "gif");
63 strcpy(mimeArray[2].type, "image/gif");
64 strcpy(mimeArray[3].ext, "jpg");
65 strcpy(mimeArray[3].type, "image/jpg");
66 strcpy(mimeArray[4].ext, "png");
67 strcpy(mimeArray[4].type, "image/png");
68 strcpy(mimeArray[5].ext, "php");
69 strcpy(mimeArray[5].type, "application/x-httpd-php");
70 mimeEntries = entries;
71 return -1;
72 }
73 entries = 0;
74 while (!feof(f)) {
75 fscanf(f, "%s", str);
76 if(!feof(f)) {
77 fscanf(f, "%s", str);
78 entries++;
79 }
80 }
81
82 /* now we rewind the file and read those values at last */
83 fseek(f, (long) 0, SEEK_SET);
84 clearerr(f);
85
86 DBGPRINTF(("attempting to read %d mime entries\n", entries));
87 /* init the mime data array */
88 mimeArray = (mimeData *) calloc(entries, sizeof(mimeData));
89 if (mimeArray == NULL) {
90 DBGPRINTF(("Error while allocating mime types Array. Exiting.\n"));
91 return -2;
92 }
93 i = 0;
94
95
96 while(i < entries && !feof(f)) {
97 fscanf(f, "%s", str);
98 strcpy(mimeArray[i].ext, str);
99 fscanf(f, "%s", str);
100 strcpy(mimeArray[i].type, str);
101 i++;
102 }
103 fclose(f);
104 mimeEntries = entries;
105 return 0;
106 }
107
analyzeExtension(mimeType,addr)108 int analyzeExtension(mimeType, addr)
109 /* get extension of given file and determine mime type */
110 /* if an error happens or otherwise the extension is not recognized then MIME_TYPE_DEFAULT is used */
111 char mimeType[MAX_MIMETYPE_LEN];
112 char addr[MAX_PATH_LEN];
113 {
114 char ext[MAX_EXTENSION_LEN];
115 int i;
116 int addrLen;
117 int extStart;
118 int extLen;
119 int found;
120
121
122 addrLen = strlen(addr);
123 /* let's find out where the extension starts */
124 i = addrLen;
125 while ((i > 0) && (addr[i] != '.') && (addr[i] != '/'))
126 i--;
127 found = (addr[i] == '.') ? YES : NO;
128 if ((i == 0) || (addrLen - i - 1 > MAX_EXTENSION_LEN))
129 {
130 ext[0] = '\0';
131 DBGPRINTF(("error recognizing extension: i = %d, extlen = %d\n", i, addrLen-i-1));
132 strcpy(mimeType, MIME_TYPE_DEFAULT);
133 logWriter(LOG_BAD_EXTENSION, NULL, 0, NULL, 0);
134 return -1;
135 }
136 if (!found)
137 {
138 strcpy(mimeType, MIME_TYPE_DEFAULT); /* default mime type */
139 return 0;
140 }
141 extStart = i + 1;
142 extLen = addrLen - extStart;
143 i = 0;
144 for (i = 0; i < extLen; i++)
145 ext[i] = addr[i + extStart];
146 ext[i] = '\0';
147
148 /* determine mime type */
149 found = NO;
150 i = 0;
151 lowerCase(ext);
152 while (i < mimeEntries && !found)
153 {
154 if (!strcmp(ext, mimeArray[i].ext))
155 {
156 strcpy(mimeType, mimeArray[i].type);
157 found = YES;
158 } else
159 i++;
160 }
161 if (!found)
162 strcpy(mimeType, MIME_TYPE_DEFAULT); /* default mime type */
163 return 0;
164 }
165
166
generateMimeHeader(sock,statusCode,contentType,statistics,sentProtocol,kind)167 int generateMimeHeader(sock, statusCode, contentType, statistics, sentProtocol, kind)
168 /* prepares and writes to the socket the required connection and MIME header */
169 /* I kept it all together for practical purposes, not for logical clarity */
170 /* it returns the bytes sent */
171 int sock;
172 int statusCode;
173 char contentType[];
174 struct stat *statistics;
175 char sentProtocol[];
176 int kind; /* if FULL_HEADER or CGI_ONLY_HEADER */
177 {
178 long int size;
179 char lastModifiedStr[256];
180 char outBuff[1024]; /* we assume that the whole header can be sent one time on socket */
181 char description[128];
182 char tempStr[1024];
183 char timeStr[256];
184 struct tm *timeStruct;
185 time_t timeTemp;
186
187
188 switch (statusCode) {
189 case 200:
190 strcpy(description, "OK");
191 break;
192 case 403:
193 strcpy(description, "Forbidden");
194 break;
195 case 404:
196 strcpy(description, "Not Found");
197 break;
198 case 500:
199 strcpy(description, "Server Error");
200 break;
201 case 501:
202 strcpy(description, "Not Implemeted");
203 break;
204 default:
205 strcpy(description, "no description");
206 break;
207 }
208 /* if we have a 0.9 conenciton we don't generate any header! */
209 if (!strcmp(sentProtocol, "HTTP/0.9"))
210 return 0; /* no header for 0.9 and we don't send any bytes */
211 sprintf(tempStr, "HTTP/1.0 %d %s\n", statusCode, description);
212 strcpy(outBuff, tempStr);
213 strcat(outBuff, "Connection: close\n");
214 time(&timeTemp);
215 timeStruct = (struct tm *) localtime(&timeTemp);
216 strftime(timeStr, 256, "%a, %d %b %Y %H:%M:%S %Z", timeStruct);
217 sprintf(tempStr, "Date: %s\n", timeStr);
218 strcat(outBuff, tempStr);
219 sprintf(tempStr, "Server: %s/%s\n", SERVER_SOFTWARE_STR, SERVER_VERSION_STR);
220 strcat(outBuff, tempStr);
221 if (kind==FULL_HEADER)
222 { /* the CGI script will append its own header so we won't append a new-line */
223 sprintf(tempStr, "Content-Type: %s\n", contentType);
224 strcat(outBuff, tempStr);
225 if (statistics)
226 {
227 size = (long int)(*statistics).st_size;
228 if (size > 0)
229 {
230 sprintf(tempStr, "Content-length: %ld\n", size);
231 strcat(outBuff, tempStr);
232 }
233 timeStruct = (struct tm *) gmtime(&(*statistics).st_mtime);
234 /* here we always output GMT as a timezone, since %Z returns locale TZ on some OS, like IRIX */
235 /* we anyway used gmtime() the line above, so it should work */
236 strftime(lastModifiedStr, 256, "%a, %d %b %Y %H:%M:%S GMT", timeStruct);
237 sprintf(tempStr, "Last-Modified: %s\n", lastModifiedStr);
238 }
239 strcat(outBuff, tempStr);
240 strcat(outBuff, "\n");
241 }
242 if (sendChunk(sock, outBuff, strlen(outBuff)) < 0)
243 DBGPRINTF(("error during header write: %d\n", errno));
244 return strlen(outBuff);
245 }
246