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