1 /*								     HTWWWStr.c
2 **	WWW RELATED STRING UTILITIES
3 **
4 **	(c) COPYRIGHT MIT 1995.
5 **	Please first read the full copyright statement in the file COPYRIGH.
6 **	@(#) $Id$
7 **
8 **      Now 13 95	Spwaned from HTString.c
9 */
10 
11 /* Library include files */
12 #include "wwwsys.h"
13 #include "WWWUtil.h"
14 #include "HTParse.h"
15 #include "HTInet.h"
16 #include "HTUser.h"
17 #include "HTWWWStr.h"					 /* Implemented here */
18 
19 PRIVATE char * months[12] = {
20     "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
21 };
22 
23 #ifndef HAVE_STRFTIME
24 PRIVATE char * wkdays[7] = {
25     "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
26 };
27 #endif
28 
29 /* ------------------------------------------------------------------------- */
30 
31 /*	Find next Field
32 **	---------------
33 **	Finds the next RFC822 token in a string
34 **	On entry,
35 **	*pstr	points to a string containing a word separated
36 **		by white white space "," ";" or "=". The word
37 **		can optionally be quoted using <"> or "<" ">"
38 **		Comments surrrounded by '(' ')' are filtered out
39 **
40 ** 	On exit,
41 **	*pstr	has been moved to the first delimiter past the
42 **		field
43 **		THE STRING HAS BEEN MUTILATED by a 0 terminator
44 **
45 **	Returns	a pointer to the first word or NULL on error
46 */
HTNextField(char ** pstr)47 PUBLIC char * HTNextField (char ** pstr)
48 {
49     char * p = *pstr;
50     char * start = NULL;
51     if (!pstr || !*pstr) return NULL;
52     while (1) {
53 	/* Strip white space and other delimiters */
54 	while (*p && (isspace((int) *p) || *p==',' || *p==';' || *p=='=')) p++;
55 	if (!*p) {
56 	    *pstr = p;
57 	    return NULL;				   	 /* No field */
58 	}
59 
60 	if (*p == '"') {				     /* quoted field */
61 	    start = ++p;
62 	    for(;*p && *p!='"'; p++)
63 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
64 	    break;			    /* kr95-10-9: needs to stop here */
65 	} else if (*p == '<') {				     /* quoted field */
66 	    start = ++p;
67 	    for(;*p && *p!='>'; p++)
68 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
69 	    break;			    /* kr95-10-9: needs to stop here */
70 	} else if (*p == '(') {					  /* Comment */
71 	    for(;*p && *p!=')'; p++)
72 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
73 	    p++;
74 	} else {					      /* Spool field */
75 	    start = p;
76 	    while(*p && !isspace((int) *p) && *p!=',' && *p!=';' && *p!='=')
77 		p++;
78 	    break;						   /* Got it */
79 	}
80     }
81     if (*p) *p++ = '\0';
82     *pstr = p;
83     return start;
84 }
85 
86 /*	Find next LWS delimited token
87 **	-----------------------------
88 **	On entry,
89 **	*pstr	points to a string containing a word separated
90 **		by white white space "," ";" or "=". The word
91 **		can optionally be quoted using <"> or "<" ">"
92 **		Comments surrrounded by '(' ')' are filtered out
93 **
94 ** 	On exit,
95 **	*pstr	has been moved to the first delimiter past the
96 **		field
97 **		THE STRING HAS BEEN MUTILATED by a 0 terminator
98 **
99 **	Returns	a pointer to the first word or NULL on error
100 */
HTNextLWSToken(char ** pstr)101 PUBLIC char * HTNextLWSToken (char ** pstr)
102 {
103     char * p = *pstr;
104     char * start = NULL;
105     if (!pstr || !*pstr) return NULL;
106 
107     /* Strip initial white space  */
108     while (*p && (isspace((int) *p))) p++;
109     if (!*p) {
110 	*pstr = p;
111 	return NULL;				   	 /* No field */
112     }
113 
114     /* Now search for the next white space */
115     start = p;
116     while(*p && !isspace((int) *p)) p++;
117 
118     if (*p) *p++ = '\0';
119     *pstr = p;
120     return start;
121 }
122 
123 /*	Find next Name-value pair
124 **	-------------------------
125 **	This is the same as HTNextField but it does not look for '=' as a
126 **	separator so if there is a name-value pair then both parts are
127 **	returned.
128 **	Returns	a pointer to the first word or NULL on error
129 */
HTNextPair(char ** pstr)130 PUBLIC char * HTNextPair (char ** pstr)
131 {
132     char * p = *pstr;
133     char * start = NULL;
134     if (!pstr || !*pstr) return NULL;
135     while (1) {
136 	/* Strip white space and other delimiters */
137 	while (*p && (*p==',' || *p==';')) p++;
138 	if (!*p) {
139 	    *pstr = p;
140 	    return NULL;				   	 /* No field */
141 	}
142 
143 	if (*p == '"') {				     /* quoted field */
144 	    start = ++p;
145 	    for(;*p && *p!='"'; p++)
146 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
147 	    break;			    /* kr95-10-9: needs to stop here */
148 	} else if (*p == '<') {				     /* quoted field */
149 	    start = ++p;
150 	    for(;*p && *p!='>'; p++)
151 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
152 	    break;			    /* kr95-10-9: needs to stop here */
153 	} else if (*p == '(') {					  /* Comment */
154 	    for(;*p && *p!=')'; p++)
155 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
156 	    p++;
157 	} else {					      /* Spool field */
158 	    start = p;
159 	    while(*p && *p!=',' && *p!=';')
160 		p++;
161 	    break;						   /* Got it */
162 	}
163     }
164     if (*p) *p++ = '\0';
165     *pstr = p;
166     return start;
167 }
168 
169 /*	Find next Name-value param
170 **	--------------------------
171 **	This is the same as HTNextPair but doesn't look for ','
172 **	Returns	a pointer to the first word or NULL on error
173 */
HTNextParam(char ** pstr)174 PUBLIC char * HTNextParam (char ** pstr)
175 {
176     char * p = *pstr;
177     char * start = NULL;
178     if (!pstr || !*pstr) return NULL;
179     while (1) {
180 	/* Strip white space and other delimiters */
181 	while (*p && *p==';') p++;
182 	if (!*p) {
183 	    *pstr = p;
184 	    return NULL;				   	 /* No field */
185 	}
186 
187 	if (*p == '"') {				     /* quoted field */
188 	    start = ++p;
189 	    for(;*p && *p!='"'; p++)
190 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
191 	    break;			    /* kr95-10-9: needs to stop here */
192 	} else if (*p == '<') {				     /* quoted field */
193 	    start = ++p;
194 	    for(;*p && *p!='>'; p++)
195 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
196 	    break;			    /* kr95-10-9: needs to stop here */
197 	} else if (*p == '(') {					  /* Comment */
198 	    for(;*p && *p!=')'; p++)
199 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
200 	    p++;
201 	} else {					      /* Spool field */
202 	    start = p;
203 	    while (*p && *p!=';')
204 		p++;
205 	    break;						   /* Got it */
206 	}
207     }
208     if (*p) *p++ = '\0';
209     *pstr = p;
210     return start;
211 }
212 
213 /*	Find next element in a comma separated string
214 **	---------------------------------------------
215 **	This is the same as HTNextPair but it does not look for anything
216 **	else than ',' as separator
217 **	Returns	a pointer to the first word or NULL on error
218 */
HTNextElement(char ** pstr)219 PUBLIC char * HTNextElement (char ** pstr)
220 {
221     char * p = *pstr;
222     char * start = NULL;
223     if (!pstr || !*pstr) return NULL;
224 
225     /* Strip white space and other delimiters */
226     while (*p && ((isspace((int) *p)) || *p==',')) p++;
227     if (!*p) {
228 	*pstr = p;
229 	return NULL;					   	 /* No field */
230     }
231     start = p;
232     while (1) {
233 	if (*p == '"') {				     /* quoted field */
234 	    for(;*p && *p!='"'; p++)
235 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
236 	    p++;
237 	} else if (*p == '<') {				     /* quoted field */
238 	    for(;*p && *p!='>'; p++)
239 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
240 	    p++;
241 	} else if (*p == '(') {					  /* Comment */
242 	    for(;*p && *p!=')'; p++)
243 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
244 	    p++;
245 	} else {					      /* Spool field */
246 	    while (*p && *p!=',') p++;
247 	    break;						   /* Got it */
248 	}
249     }
250     if (*p) *p++ = '\0';
251     *pstr = p;
252     return start;
253 }
254 
255 /*	Find next "/" delimied segment
256 **	------------------------------
257 **	This is the same as HTNextField but it includes "/" as a delimiter.
258 **	Returns	a pointer to the first segment or NULL on error
259 */
HTNextSegment(char ** pstr)260 PUBLIC char * HTNextSegment (char ** pstr)
261 {
262     char * p = *pstr;
263     char * start = NULL;
264     if (!pstr || !*pstr) return NULL;
265     while (1) {
266 	/* Strip white space and other delimiters */
267 	while (*p && (isspace((int) *p) || *p==',' || *p==';' || *p=='=' || *p=='/')) p++;
268 	if (!*p) {
269 	    *pstr = p;
270 	    return NULL;				   	 /* No field */
271 	}
272 
273 	if (*p == '"') {				     /* quoted field */
274 	    start = ++p;
275 	    for(;*p && *p!='"'; p++)
276 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
277 	    break;			    /* kr95-10-9: needs to stop here */
278 	} else if (*p == '<') {				     /* quoted field */
279 	    start = ++p;
280 	    for(;*p && *p!='>'; p++)
281 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
282 	    break;			    /* kr95-10-9: needs to stop here */
283 	} else if (*p == '(') {					  /* Comment */
284 	    for(;*p && *p!=')'; p++)
285 		if (*p == '\\' && *(p+1)) p++;	       /* Skip escaped chars */
286 	    p++;
287 	} else {					      /* Spool field */
288 	    start = p;
289 	    while(*p && !isspace((int) *p) && *p!=',' && *p!=';' && *p!='=' && *p!='/')
290 		p++;
291 	    break;						   /* Got it */
292 	}
293     }
294     if (*p) *p++ = '\0';
295     *pstr = p;
296     return start;
297 }
298 
299 /*
300 **	Find the next s-expression token from a string of characters.
301 **	We return the name of this expression and the param points to the
302 **	parameters.
303 **
304 **	NOTE: The string has been mutilated by '/0's
305 */
HTNextSExp(char ** exp,char ** param)306 PUBLIC char * HTNextSExp (char ** exp, char ** param)
307 {
308     char * p = *exp;
309     char * name = NULL;
310     if (!exp || !*exp) return NULL;
311     while (*p && isspace((int) *p)) p++;		/* Strip leading white space */
312     if (!*p) {
313 	*exp = p;
314 	return NULL;					   	 /* No field */
315     }
316     if (*p == '{') {					     /* Open bracket */
317 	int cnt = 1;
318 	/*
319 	**  Look for name of this expression. If we find a token then search
320 	**  for the rest of the expression and remove the end '}'
321 	*/
322 	p++;
323 	if ((name = HTNextField(&p)) == NULL) return NULL;
324 	while (*p && isspace((int) *p)) p++;
325 	*param = p;
326 	while (*p) {
327 	    if (*p == '{') cnt++;
328 	    if (*p == '}') cnt--;
329 	    if (!cnt) {
330 		*p = '\0';
331 		break;
332 	    }
333 	    p++;
334 	}
335     }
336     return name;
337 }
338 
339 /*
340 **	Returns a Message-ID string including the open '<' and the closing '>'.
341 **	The format of the string is:
342 **
343 **		"<" time-in-sec "Z" process-id "@" FQDN ">"
344 **
345 **	or on systems that doesn't have process-id:
346 **
347 **		"<" time-in-sec "Z" user "@" FQDN ">"
348 **
349 **	Returns a pointer to the MessageID
350 */
HTMessageIdStr(HTUserProfile * up)351 PUBLIC const char * HTMessageIdStr (HTUserProfile * up)
352 {
353     static char buf[80];
354     time_t sectime = time(NULL);
355 #ifdef HAVE_GETPID
356     const char * address = HTUserProfile_fqdn(up);
357 #else
358     const char * address = HTUserProfile_email(up);
359 #endif /* HAVE_GETPID */
360     if (!address) address = tmpnam(NULL);
361     if ((!address || !*address) && sectime < 0) {
362 	HTTRACE(CORE_TRACE, "MessageID...  Can't make a unique MessageID\n");
363 	return "";
364     }
365 #ifdef HAVE_GETPID
366     sprintf(buf, "<%ldZ%ld@%s>", (long)sectime, (long)getpid(), address ? address : "@@@");
367 #else
368     sprintf(buf, "<%ldZ%s>", sectime, address ? address : "@@@");
369 #endif /* HAVE_GETPID */
370 
371     *(buf+79) = '\0';
372     return buf;
373 }
374 
375 /*	Date and Time Parser
376 **	--------------------
377 **	These functions are taken from the server written by Ari Luotonen
378 */
379 #if 0
380 PRIVATE int make_num (const char *  s)
381 {
382     if (*s >= '0' && *s <= '9')
383 	return 10 * (*s - '0') + *(s+1) - '0';
384     else
385 	return *(s+1) - '0';
386 }
387 #endif
make_month(char * s,char ** ends)388 PRIVATE int make_month (char * s, char ** ends)
389 {
390     char * ptr = s;
391     while (!isalpha((int) *ptr)) ptr++;
392     if (*ptr) {
393 	int i;
394 	*ends = ptr+3;
395 	for (i=0; i<12; i++)
396 	    if (!strncasecomp(months[i], ptr, 3)) return i;
397     }
398     return 0;
399 }
400 
401 /*
402 **	Parse a str in GMT format to a local time time_t representation
403 **	Four formats are accepted:
404 **
405 **		Wkd, 00 Mon 0000 00:00:00 GMT		(rfc1123)
406 **		Weekday, 00-Mon-00 00:00:00 GMT		(rfc850)
407 **		Wkd Mon 00 00:00:00 0000 GMT		(ctime)
408 **		1*DIGIT					(delta-seconds)
409 */
HTParseTime(const char * str,HTUserProfile * up,BOOL expand)410 PUBLIC time_t HTParseTime (const char * str, HTUserProfile * up, BOOL expand)
411 {
412     char * s;
413     struct tm tm;
414     time_t t;
415 
416     if (!str) return 0;
417 
418     if ((s = strchr(str, ','))) {	 /* Thursday, 10-Jun-93 01:29:59 GMT */
419 	s++;				/* or: Thu, 10 Jan 1993 01:29:59 GMT */
420 	while (*s && *s==' ') s++;
421 	if (strchr(s,'-')) {				     /* First format */
422 	    HTTRACE(CORE_TRACE, "Format...... Weekday, 00-Mon-00 00:00:00 GMT\n");
423 	    if ((int)strlen(s) < 18) {
424 		HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format \"%s\"\n" _ s);
425 		return 0;
426 	    }
427 	    tm.tm_mday = strtol(s, &s, 10);
428 	    tm.tm_mon = make_month(s, &s);
429 	    tm.tm_year = strtol(++s, &s, 10);
430 	    tm.tm_hour = strtol(s, &s, 10);
431 	    tm.tm_min = strtol(++s, &s, 10);
432 	    tm.tm_sec = strtol(++s, &s, 10);
433 
434 	} else {					    /* Second format */
435 	    HTTRACE(CORE_TRACE, "Format...... Wkd, 00 Mon 0000 00:00:00 GMT\n");
436 	    if ((int)strlen(s) < 20) {
437 		HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format \"%s\"\n" _ s);
438 		return 0;
439 	    }
440 	    tm.tm_mday = strtol(s, &s, 10);
441 	    tm.tm_mon = make_month(s, &s);
442 	    tm.tm_year = strtol(s, &s, 10) - 1900;
443 	    tm.tm_hour = strtol(s, &s, 10);
444 	    tm.tm_min = strtol(++s, &s, 10);
445 	    tm.tm_sec = strtol(++s, &s, 10);
446 	}
447     } else if (isdigit((int) *str)) {
448 
449 	if (strchr(str, 'T')) {		        /* ISO (limited format) date string */
450 	    HTTRACE(CORE_TRACE, "Format...... YYYY.MM.DDThh:mmStzWkd\n");
451 	    s = (char *) str;
452 	    while (*s && *s==' ') s++;
453 	    if ((int)strlen(s) < 21) {
454 		HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format `%s\'\n" _ s);
455 		return 0;
456 	    }
457 	    tm.tm_year = strtol(s, &s, 10) - 1900;
458 	    tm.tm_mon  = strtol(++s, &s, 10);
459 	    tm.tm_mday = strtol(++s, &s, 10);
460 	    tm.tm_hour = strtol(++s, &s, 10);
461 	    tm.tm_min  = strtol(++s, &s, 10);
462 	    tm.tm_sec  = strtol(++s, &s, 10);
463 
464 	} else {					    /* delta seconds */
465 	    t = expand ? time(NULL) + atol(str) : atol(str);
466 
467 #ifdef HTDEBUG
468 	    if (CORE_TRACE) {
469 		if (expand) {
470 #if defined (HAVE_CTIME_R_2)
471 		    char buffer[CTIME_MAX];
472 		    HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds, or in local time: %s" _
473 			    str _ (long) t _ (char *) ctime_r(&t, buffer));
474 #elif defined(HAVE_CTIME_R_3)
475 		    char buffer[CTIME_MAX];
476 		    HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds, or in local time: %s" _
477 			    str _ (long) t _ (char *) ctime_r(&t, buffer, CTIME_MAX));
478 #else
479 		    HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds, or in local time: %s" _
480 			    str _ (long) t _ ctime(&t));
481 #endif /* HT_REENTRANT */
482 		} else {
483 		    HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds\n" _ str _ (long) t);
484 		}
485 	    }
486 #endif /* HT_DEBUG */
487 	    return t;
488 	}
489 
490     } else {	      /* Try the other format:  Wed Jun  9 01:29:59 1993 GMT */
491 	HTTRACE(CORE_TRACE, "Format...... Wkd Mon 00 00:00:00 0000 GMT\n");
492 	s = (char *) str;
493 	while (*s && *s==' ') s++;
494 	HTTRACE(CORE_TRACE, "Trying...... The Wrong time format: %s\n" _ s);
495 	if ((int)strlen(s) < 24) {
496 	    HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format \"%s\"\n" _ s);
497 	    return 0;
498 	}
499 	tm.tm_mon = make_month(s, &s);
500 	tm.tm_mday = strtol(s, &s, 10);
501 	tm.tm_hour = strtol(s, &s, 10);
502 	tm.tm_min = strtol(++s, &s, 10);
503 	tm.tm_sec = strtol(++s, &s, 10);
504 	tm.tm_year = strtol(s, &s, 10) - 1900;
505     }
506     if (tm.tm_sec  < 0  ||  tm.tm_sec  > 59  ||
507 	tm.tm_min  < 0  ||  tm.tm_min  > 59  ||
508 	tm.tm_hour < 0  ||  tm.tm_hour > 23  ||
509 	tm.tm_mday < 1  ||  tm.tm_mday > 31  ||
510 	tm.tm_mon  < 0  ||  tm.tm_mon  > 11  ||
511 	tm.tm_year <70  ||  tm.tm_year >120) {
512 	HTTRACE(CORE_TRACE, "ERROR....... Parsed illegal time: %02d.%02d.%02d %02d:%02d:%02d\n" _
513 	       tm.tm_mday _ tm.tm_mon+1 _ tm.tm_year _
514 	       tm.tm_hour _ tm.tm_min _ tm.tm_sec);
515 	return 0;
516     }
517 
518 #if 0
519 #if defined(HAVE_TIMEZONE) && defined(HAVE_ALTZONE)
520     tm.tm_isdst = daylight;		       /* Already taken into account */
521     HTTRACE(CORE_TRACE, "Time string. Daylight is %s\n" _
522 	    daylight>0 ? "on" : daylight==0 ? "off" : "unknown");
523 #endif
524 #else
525     /* Let mktime decide whether we have DST or not */
526     tm.tm_isdst = -1;
527 #endif
528 
529 #ifdef HAVE_MKTIME
530     t = mktime(&tm);
531     t += (up ? HTUserProfile_timezone(up) : HTGetTimeZoneOffset());
532 #else
533 #ifdef HAVE_TIMEGM
534     t = timegm(&tm);
535 #else
536 #error "Neither mktime nor timegm defined"
537 #endif /* HAVE_TIMEGM */
538 #endif /* HAVE_MKTIME */
539 
540     HTTRACE(CORE_TRACE, "Time string. %s parsed to %ld calendar time or `%s' in local time\n" _
541 		str _ (long) t _ ctime(&t));
542     return t;
543 }
544 
545 /*
546 **	Returns a string pointer to a static area of the current calendar
547 **	time in RFC 1123 format, for example
548 **
549 **		Sun, 06 Nov 1994 08:49:37 GMT
550 **
551 **	The result can be given in both local and GMT dependent on the flag
552 */
HTDateTimeStr(time_t * calendar,BOOL local)553 PUBLIC const char *HTDateTimeStr (time_t * calendar, BOOL local)
554 {
555     static char buf[40];
556 
557 #ifdef HAVE_STRFTIME
558     if (local) {
559 	/*
560 	** Solaris 2.3 has a bug so we _must_ use reentrant version
561 	** Thomas Maslen <tmaslen@verity.com>
562 	*/
563 #if defined(HT_REENTRANT) || defined(SOLARIS)
564 	struct tm loctime;
565 	localtime_r(calendar, &loctime);
566 	strftime(buf, 40, "%a, %d %b %Y %H:%M:%S", &loctime);
567 #else
568 	struct tm *loctime = localtime(calendar);
569 	strftime(buf, 40, "%a, %d %b %Y %H:%M:%S", loctime);
570 #endif /* SOLARIS || HT_REENTRANT */
571     } else {
572 #if defined(HT_REENTRANT) || defined(SOLARIS)
573 	struct tm gmt;
574 	gmtime_r(calendar, &gmt);
575     	strftime(buf, 40, "%a, %d %b %Y %H:%M:%S GMT", &gmt);
576 #else
577 	struct tm *gmt = gmtime(calendar);
578     	strftime(buf, 40, "%a, %d %b %Y %H:%M:%S GMT", gmt);
579 #endif /* SOLARIS || HT_REENTRANT */
580     }
581 #else
582     if (local) {
583 #if defined(HT_REENTRANT)
584 	struct tm loctime;
585 	localtime_r(calendar, &loctime);
586 #else
587 	struct tm *loctime = localtime(calendar);
588 #endif /* HT_REENTRANT */
589 	sprintf(buf,"%s, %02d %s %04d %02d:%02d:%02d",
590 		wkdays[loctime->tm_wday],
591 		loctime->tm_mday,
592 		months[loctime->tm_mon],
593 		loctime->tm_year + 1900,
594 		loctime->tm_hour,
595 		loctime->tm_min,
596 		loctime->tm_sec);
597     } else {
598 #if defined(HT_REENTRANT) || defined(SOLARIS)
599 	struct tm gmt;
600 	gmtime_r(calendar, &gmt);
601 #else
602 	struct tm *gmt = gmtime(calendar);
603 #endif
604 	sprintf(buf,"%s, %02d %s %04d %02d:%02d:%02d GMT",
605 		wkdays[gmt->tm_wday],
606 		gmt->tm_mday,
607 		months[gmt->tm_mon],
608 		gmt->tm_year + 1900,
609 		gmt->tm_hour,
610 		gmt->tm_min,
611 		gmt->tm_sec);
612     }
613 #endif
614     return buf;
615 }
616 
617 /*	HTDateDirStr
618 **	------------
619 **	Generates a date string used in directory listings
620 */
HTDateDirStr(time_t * time,char * str,int len)621 PUBLIC BOOL HTDateDirStr (time_t * time, char * str, int len)
622 {
623 #ifdef HAVE_STRFTIME
624 #if defined(HT_REENTRANT) || defined(SOLARIS)
625     struct tm loctime;
626     localtime_r(time, &loctime);
627     strftime(str, len, "%d-%b-%Y %H:%M", &loctime);
628     return YES;
629 #else
630     strftime(str, len, "%d-%b-%Y %H:%M", localtime(time));
631     return YES;
632 #endif /* HT_REENTRANT || SOLARIS */
633 #else
634     if (len >= 16) {
635 	struct tm *loctime = localtime(time);
636 	sprintf(str,"%02d-%s-%02d %02d:%02d",
637 		loctime->tm_mday,
638 		months[loctime->tm_mon],
639 		loctime->tm_year % 100,
640 		loctime->tm_hour,
641 		loctime->tm_min);
642 	return YES;
643     }
644     return NO;
645 #endif /* HAVE_STRFTIME */
646 }
647 
648 /* 							     	HTNumToStr
649 **	Converts a long (byte count) to a string
650 **	----------------------------------------
651 **	This function was a PAIN!  In computer-world 1K is 1024 bytes
652 **	and 1M is 1024K -- however, sprintf() still formats in base-10.
653 **	Therefore I output only until 999, and then start using the
654 **	next unit.  This doesn't work wrong, it's just a feature.
655 **	The "str" must be large enough to contain the result.
656 */
HTNumToStr(unsigned long n,char * str,int len)657 PUBLIC void HTNumToStr (unsigned long n, char * str, int len)
658 {
659     double size = n/1024.0;
660     if (len < 6) {
661 	*str = '\0';
662 	return;
663     }
664     if (n < 1000)
665 	sprintf(str, "%dK", n>0 ? 1 : 0);
666     else if (size + 0.999 < 1000)
667 	sprintf(str, "%dK", (int)(size + 0.5));
668     else if ((size /= 1024) < 9.9)
669 	sprintf(str, "%.1fM", (size + 0.05));
670     else if (size < 1000)
671 	sprintf(str, "%dM", (int)(size + 0.5));
672     else if ((size /= 1024) < 9.9)
673 	sprintf(str, "%.1fG", (size + 0.05));
674     else
675 	sprintf(str, "%dG", (int)(size + 0.5));
676 }
677 
678 /*
679 **	Matches MIME constructions for content-types and others like
680 **	them, for example "text/html", "text/plain". It can also match
681 **	wild cards like "text/<star>" and "<star>/<star>. We use <star>
682 **	instead of * in order note to make C like comments :-)
683 */
HTMIMEMatch(HTAtom * tmplate,HTAtom * actual)684 PUBLIC BOOL HTMIMEMatch (HTAtom * tmplate, HTAtom * actual)
685 {
686     const char *t, *a;
687     char *st, *sa;
688     BOOL match = NO;
689 
690     if (tmplate && actual && (t = HTAtom_name(tmplate))) {
691 	if (!strcmp(t, "*"))
692 	    return YES;
693 
694 	if (strchr(t, '*') &&
695 	    (a = HTAtom_name(actual)) &&
696 	    (st = strchr(t, '/')) && (sa = strchr(a,'/'))) {
697 
698 	    *sa = 0;
699 	    *st = 0;
700 
701 	    if ((*(st-1)=='*' &&
702 		 (*(st+1)=='*' || !strcasecomp(st+1, sa+1))) ||
703 		(*(st+1)=='*' && !strcasecomp(t,a)))
704 		match = YES;
705 
706 	    *sa = '/';
707 	    *st = '/';
708 	}
709     }
710     return match;
711 }
712 
713 /*	Convert file URLs into a local representation
714 **	---------------------------------------------
715 **	The URL has already been translated through the rules in get_physical
716 **	in HTAccess.c and all we need to do now is to map the path to a local
717 **	representation, for example if must translate '/' to the ones that
718 **	turn the wrong way ;-)
719 **	Returns:
720 **		OK:	local file (that must be freed by caller)
721 **		Error:	NULL
722 */
HTWWWToLocal(const char * url,const char * base,HTUserProfile * up)723 PUBLIC char * HTWWWToLocal (const char * url, const char * base,
724 			    HTUserProfile * up)
725 {
726     if (url) {
727 	char * access = HTParse(url, base, PARSE_ACCESS);
728 	char * host = HTParse(url, base, PARSE_HOST);
729 	char * path = HTParse(url, base, PARSE_PATH+PARSE_PUNCTUATION);
730 	const char * myhost = HTUserProfile_fqdn(up);
731 
732 	/* Find out if this is a reference to the local file system */
733 	if ((*access && strcmp(access, "file") && strcmp(access, "cache")) ||
734 	     (*host && strcasecomp(host, "localhost") &&
735 	      myhost && strcmp(host, myhost))) {
736 	    HTTRACE(CORE_TRACE, "LocalName... Not on local file system\n");
737 	    HT_FREE(access);
738 	    HT_FREE(host);
739 	    HT_FREE(path);
740 	    return NULL;
741 	} else {
742 	    char *ptr;
743 	    if ((ptr = strchr(path, ';')) || (ptr = strchr(path, '?')))
744 		*ptr = '\0';
745 
746 	    /*
747 	    ** Do whatever translation is required here in order to fit your
748 	    ** platform _before_ the path is unescaped.
749 	    */
750 #ifdef VMS
751 	    HTVMS_checkDecnet(path);
752 #endif
753 #ifdef WWW_MSWINDOWS
754 	    /* An absolute pathname with logical drive */
755             if (*path == '/' && path[2] == ':') {
756 		char *orig=path, *dest=path+1;
757 		while((*orig++ = *dest++));
758 
759 	    /* A network host */
760             } else if (*host && strcasecomp(host, "localhost")) {
761 		char * newpath = NULL;
762 		StrAllocMCopy(&newpath, "//", host, path, NULL);
763 		HT_FREE(path);
764 		path = newpath;
765 	    }
766 
767 	    /* Convert '/' to '\' */
768 	    {
769 		char *p = path;
770 		while (*p) {
771 		    if (*p=='/') *p='\\';
772 		    p++;
773 		}
774 	    }
775 #endif
776 
777 	    HTUnEscape(path);		  /* Take out the escaped characters */
778 	    HTTRACE(CORE_TRACE, "Node........ `%s' means path `%s'\n" _ url _ path);
779 	    HT_FREE(access);
780 	    HT_FREE(host);
781 	    return path;
782 	}
783     }
784     return NULL;
785 }
786 
787 /*	Convert a local file name into a URL
788 **	------------------------------------
789 **	Generates a WWW URL name from a local file name or NULL if error.
790 **	Returns:
791 **		OK:	local file (that must be freed by caller)
792 **		Error:	NULL
793 */
HTLocalToWWW(const char * local,const char * access)794 PUBLIC char * HTLocalToWWW (const char * local, const char * access)
795 {
796     char * escaped = NULL;
797     const char * scheme = (access && *access) ? access : "file:";
798     if (local && *local) {
799 #ifdef VMS
800         char * unescaped = NULL;
801         if ((unescaped = (char *) HT_MALLOC(strlen(local) + 10)) == NULL)
802             HT_OUTOFMEM("HTLocalToWWW");
803         strcpy(unescaped, scheme);	     /* We get an absolute file name */
804 
805 	/* convert directory name to Unix-style syntax */
806 	{
807 	    char * disk = strchr (local, ':');
808 	    char * dir = strchr (local, '[');
809 	    if (disk) {
810 		*disk = '\0';
811 		strcat(unescaped, "/");
812 		strcat(unescaped, local);
813 	    }
814 	    if (dir) {
815 		char *p;
816 		*dir = '/';	/* Convert leading '[' */
817 		for (p = dir ; *p != ']'; ++p)
818 		    if (*p == '.') *p = '/';
819 		*p = '\0';	/* Cut on final ']' */
820 		strcat(unescaped, dir);
821 	    }
822 	}
823         escaped = HTEscape(unescaped, URL_DOSFILE);
824         HT_FREE(unescaped);
825 
826 #else  /* not VMS */
827 #ifdef WIN32
828         char * unescaped = NULL;
829         if ((unescaped = (char *) HT_MALLOC(strlen(local) + 10)) == NULL)
830             HT_OUTOFMEM("HTLocalToWWW");
831         strcpy(unescaped, scheme);	     /* We get an absolute file name */
832         if (strchr(local, ':')) strcat(unescaped, "/");
833         {
834             const char *p = local;
835             char *q = unescaped+strlen(unescaped);
836             while (*p) {
837                 if (*p=='\\') {
838                     *q++='/';
839                 } else
840                     *q++=*p;
841                 p++;
842             }
843             *q = '\0';
844 	}
845         escaped = HTEscape(unescaped, URL_DOSFILE);
846         HT_FREE(unescaped);
847 
848 #else  /* Unix */
849         char * escaped_path = HTEscape(local, URL_PATH);
850 	escaped = StrAllocMCopy(&escaped, scheme, escaped_path, NULL);
851         HT_FREE(escaped_path);
852 
853 #endif /* not WIN32 */
854 #endif /* not VMS */
855     }
856     return escaped;
857 }
858