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