Annotation of libwww/Library/src/HTWWWStr.c, revision 2.9
2.1 frystyk 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: **
7: ** Now 13 95 Spwaned from HTString.c
8: */
9:
10: /* Library include files */
2.8 frystyk 11: #include "sysdep.h"
2.1 frystyk 12: #include "HTUtils.h"
13: #include "HTString.h"
14: #include "HTTCP.h"
15: #include "HTParse.h"
16: #include "HTWWWStr.h" /* Implemented here */
17:
18: PRIVATE long HTTimeZone = 0L; /* Offset from GMT in seconds */
19: PRIVATE char * months[12] = {
20: "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
21: };
22:
2.8 frystyk 23: #ifndef HAVE_STRFTIME
2.1 frystyk 24: PRIVATE char * wkdays[7] = {
25: "Mon","Tue","Wed","Thu","Fri","Sat","Sun"
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: */
47: PUBLIC char * HTNextField (char ** pstr)
48: {
49: char * p = *pstr;
50: char * start = NULL;
2.4 frystyk 51: if (!*pstr) return NULL;
2.1 frystyk 52: while (1) {
53: /* Strip white space and other delimiters */
54: while (*p && (WHITE(*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 && !WHITE(*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: /*
87: ** Returns a Message-ID string including the open '<' and the closing '>'.
88: ** The format of the string is:
89: **
90: ** "<" time-in-sec "Z" process-id "@" FQDN ">"
91: **
92: ** or on systems that doesn't have process-id:
93: **
94: ** "<" time-in-sec "Z" user "@" FQDN ">"
95: **
96: ** Returns a pointer to the MessageID
97: */
2.8 frystyk 98: PUBLIC const char *HTMessageIdStr (void)
2.1 frystyk 99: {
100: static char buf[80];
101: time_t sectime = time(NULL);
2.8 frystyk 102: #ifdef HAVE_GETPID
103: const char *address = HTGetDomainName();
2.1 frystyk 104: #else
2.8 frystyk 105: const char *address = HTGetMailAddress();
106: #endif /* HAVE_GETPID */
2.1 frystyk 107: if (!address) address = tmpnam(NULL);
108: if ((!address || !*address) && sectime < 0) {
2.9 ! hallam 109: if (CORE_TRACE)
2.6 eric 110: HTTrace("MessageID... Can't make a unique MessageID\n");
2.1 frystyk 111: return "";
112: }
2.8 frystyk 113: #ifdef HAVE_GETPID
2.7 frystyk 114: sprintf(buf, "<%ldZ%ld@%s>", sectime, getpid(), address ? address : "@@@");
2.1 frystyk 115: #else
116: sprintf(buf, "<%ldZ%s>", sectime, address ? address : "@@@");
2.8 frystyk 117: #endif /* HAVE_GETPID */
2.1 frystyk 118:
119: *(buf+79) = '\0';
120: return buf;
121: }
122:
123: /* Date and Time Parser
124: ** --------------------
125: ** These functions are taken from the server written by Ari Luotonen
126: */
127:
2.8 frystyk 128: PRIVATE int make_num (const char * s)
2.1 frystyk 129: {
130: if (*s >= '0' && *s <= '9')
131: return 10 * (*s - '0') + *(s+1) - '0';
132: else
133: return *(s+1) - '0';
134: }
135:
2.8 frystyk 136: PRIVATE int make_month (const char * s)
2.1 frystyk 137: {
138: int i;
139: for (i=0; i<12; i++)
140: if (!strncasecomp(months[i], s, 3))
141: return i;
142: return 0;
143: }
144:
145: /* Timezone Offset
146: ** ---------------
147: ** Calculates the offset from GMT in seconds
148: */
149: PUBLIC long HTGetTimeZoneOffset (void)
150: {
2.8 frystyk 151: #ifdef HAVE_TIMEZONE
2.1 frystyk 152: {
153: time_t cur_t = time(NULL);
154: #ifdef HT_REENTRANT
155: struct tm loctime;
156: struct tm *local = (struct tm *) localtime_r(&cur_t, &loctime);
157: #else
158: struct tm *local = localtime(&cur_t);
159: #endif /* HT_REENTRANT */
160: if (daylight && local->tm_isdst>0) { /* daylight time? */
2.8 frystyk 161: #ifdef HAVE_ALTZONE
2.1 frystyk 162: HTTimeZone = altzone;
163: #else
164: /* Assumes a fixed DST offset of 1 hour, which is probably wrong */
165: HTTimeZone = timezone - 3600;
2.8 frystyk 166: #endif /* HAVE_ALTZONE */
2.1 frystyk 167: } else { /* no */
168: HTTimeZone = timezone;
169: }
170: HTTimeZone = -HTTimeZone;
2.9 ! hallam 171: if (CORE_TRACE)
2.6 eric 172: HTTrace("TimeZone.... GMT + (%02d) hours (including DST)\n",
2.1 frystyk 173: (int) HTTimeZone/3600);
174: }
175: #else
2.8 frystyk 176: #ifdef HAVE_TM_GMTOFF
2.1 frystyk 177: {
178: time_t cur_t = time(NULL);
179: #ifdef HT_REENTRANT
180: struct tm loctime;
181: localtime_r(&cur_t, &loctime);
182: #else
183: struct tm * local = localtime(&cur_t);
184: #endif /* HT_REENTRANT */
185: HTTimeZone = local->tm_gmtoff;
2.9 ! hallam 186: if (CORE_TRACE)
2.6 eric 187: HTTrace("TimeZone.... GMT + (%02d) hours (including DST)\n",
2.1 frystyk 188: (int)local->tm_gmtoff / 3600);
189: }
190: #else
2.9 ! hallam 191: if (CORE_TRACE) HTTrace("TimeZone.... Not defined\n");
2.8 frystyk 192: #endif /* HAVE_TM_GMTOFF */
193: #endif /* HAVE_TIMEZONE */
2.1 frystyk 194: return HTTimeZone;
195: }
196:
197: /*
198: ** Parse a str in GMT format to a local time time_t representation
199: ** Four formats are accepted:
200: **
201: ** Wkd, 00 Mon 0000 00:00:00 GMT (rfc1123)
202: ** Weekday, 00-Mon-00 00:00:00 GMT (rfc850)
203: ** Wkd Mon 00 00:00:00 0000 GMT (ctime)
204: ** 1*DIGIT (delta-seconds)
205: */
2.8 frystyk 206: PUBLIC time_t HTParseTime (const char * str)
2.1 frystyk 207: {
2.8 frystyk 208: const char * s;
2.1 frystyk 209: struct tm tm;
210: time_t t;
211:
212: if (!str) return 0;
213:
214: if ((s = strchr(str, ','))) { /* Thursday, 10-Jun-93 01:29:59 GMT */
215: s++; /* or: Thu, 10 Jan 1993 01:29:59 GMT */
216: while (*s && *s==' ') s++;
217: if (strchr(s,'-')) { /* First format */
2.9 ! hallam 218: if (CORE_TRACE)
2.6 eric 219: HTTrace("Format...... Weekday, 00-Mon-00 00:00:00 GMT\n");
2.1 frystyk 220: if ((int)strlen(s) < 18) {
2.9 ! hallam 221: if (CORE_TRACE)
2.6 eric 222: HTTrace(
2.1 frystyk 223: "ERROR....... Not a valid time format \"%s\"\n",s);
224: return 0;
225: }
226: tm.tm_mday = make_num(s);
227: tm.tm_mon = make_month(s+3);
228: tm.tm_year = make_num(s+7);
229: tm.tm_hour = make_num(s+10);
230: tm.tm_min = make_num(s+13);
231: tm.tm_sec = make_num(s+16);
232: } else { /* Second format */
2.9 ! hallam 233: if (CORE_TRACE)
2.6 eric 234: HTTrace("Format...... Wkd, 00 Mon 0000 00:00:00 GMT\n");
2.1 frystyk 235: if ((int)strlen(s) < 20) {
2.9 ! hallam 236: if (CORE_TRACE)
2.6 eric 237: HTTrace(
2.1 frystyk 238: "ERROR....... Not a valid time format \"%s\"\n",s);
239: return 0;
240: }
241: tm.tm_mday = make_num(s);
242: tm.tm_mon = make_month(s+3);
243: tm.tm_year = (100*make_num(s+7) - 1900) + make_num(s+9);
244: tm.tm_hour = make_num(s+12);
245: tm.tm_min = make_num(s+15);
246: tm.tm_sec = make_num(s+18);
247:
248: }
249: } else if (isdigit(*str)) { /* delta seconds */
250: t = time(NULL) + atol(str); /* Current local calendar time */
2.9 ! hallam 251: if (CORE_TRACE) {
2.1 frystyk 252: #ifdef HT_REENTRANT
253: char buffer[CTIME_MAX];
2.6 eric 254: HTTrace("Time string. Delta-time %s parsed to %ld seconds, or in local time: %s", str, (long) t, (char *) ctime_r(&t, buffer, CTIME_MAX));
2.1 frystyk 255: #else
2.6 eric 256: HTTrace("Time string. Delta-time %s parsed to %ld seconds, or in local time: %s", str, (long) t, ctime(&t));
2.1 frystyk 257: #endif
258: }
259: return t;
260:
261: } else { /* Try the other format: Wed Jun 9 01:29:59 1993 GMT */
2.9 ! hallam 262: if (CORE_TRACE)
2.6 eric 263: HTTrace("Format...... Wkd Mon 00 00:00:00 0000 GMT\n");
2.1 frystyk 264: s = str;
265: while (*s && *s==' ') s++;
2.9 ! hallam 266: if (CORE_TRACE)
2.6 eric 267: HTTrace("Trying...... The Wrong time format: %s\n", s);
2.1 frystyk 268: if ((int)strlen(s) < 24) {
2.9 ! hallam 269: if (CORE_TRACE)
2.6 eric 270: HTTrace("ERROR....... Not a valid time format \"%s\"\n",s);
2.1 frystyk 271: return 0;
272: }
273: tm.tm_mday = make_num(s+8);
274: tm.tm_mon = make_month(s+4);
275: tm.tm_year = make_num(s+22);
276: tm.tm_hour = make_num(s+11);
277: tm.tm_min = make_num(s+14);
278: tm.tm_sec = make_num(s+17);
279: }
280: if (tm.tm_sec < 0 || tm.tm_sec > 59 ||
281: tm.tm_min < 0 || tm.tm_min > 59 ||
282: tm.tm_hour < 0 || tm.tm_hour > 23 ||
283: tm.tm_mday < 1 || tm.tm_mday > 31 ||
284: tm.tm_mon < 0 || tm.tm_mon > 11 ||
285: tm.tm_year <70 || tm.tm_year >120) {
2.9 ! hallam 286: if (CORE_TRACE) HTTrace(
2.1 frystyk 287: "ERROR....... Parsed illegal time: %02d.%02d.%02d %02d:%02d:%02d\n",
288: tm.tm_mday, tm.tm_mon+1, tm.tm_year,
289: tm.tm_hour, tm.tm_min, tm.tm_sec);
290: return 0;
291: }
292:
2.8 frystyk 293: #if defined(HAVE_TIMEZONE) && defined(HAVE_ALTZONE)
2.1 frystyk 294: tm.tm_isdst = daylight; /* Already taken into account */
295: #else
296: tm.tm_isdst = -1;
297: #endif
298:
2.8 frystyk 299: #ifdef HAVE_MKTIME
2.1 frystyk 300: t = mktime(&tm);
301: t += (HTTimeZone);
302: #else
2.8 frystyk 303: #ifdef HAVE_TIMEGM
2.1 frystyk 304: t = timegm(&tm);
305: #else
2.8 frystyk 306: #error "Neither mktime nor timegm defined"
307: #endif /* HAVE_TIMEGM */
308: #endif /* HAVE_MKTIME */
2.1 frystyk 309:
2.9 ! hallam 310: if (CORE_TRACE)
2.6 eric 311: HTTrace(
2.1 frystyk 312: "Time string. %s parsed to %ld seconds, or in local time: %s",
313: str, (long) t, ctime(&t));
314: return t;
315: }
316:
317: /*
318: ** Returns a string pointer to a static area of the current calendar
319: ** time in RFC 1123 format, for example
320: **
321: ** Sun, 06 Nov 1994 08:49:37 GMT
322: **
323: ** The result can be given in both local and GMT dependent on the flag
324: */
2.8 frystyk 325: PUBLIC const char *HTDateTimeStr (time_t * calendar, BOOL local)
2.1 frystyk 326: {
327: static char buf[40];
328:
2.8 frystyk 329: #ifdef HAVE_STRFTIME
2.1 frystyk 330: if (local) {
331: /*
332: ** Solaris 2.3 has a bug so we _must_ use reentrant version
333: ** Thomas Maslen <tmaslen@verity.com>
334: */
335: #if defined(HT_REENTRANT) || defined(SOLARIS)
336: struct tm loctime;
337: localtime_r(calendar, &loctime);
338: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S", &loctime);
339: #else
340: struct tm *loctime = localtime(calendar);
341: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S", loctime);
342: #endif /* SOLARIS || HT_REENTRANT */
343: } else {
344: #if defined(HT_REENTRANT) || defined(SOLARIS)
345: struct tm gmt;
346: gmtime_r(calendar, &gmt);
347: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S GMT", &gmt);
348: #else
349: struct tm *gmt = gmtime(calendar);
350: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S GMT", gmt);
351: #endif /* SOLARIS || HT_REENTRANT */
352: }
353: #else
354: if (local) {
355: #if defined(HT_REENTRANT)
356: struct tm loctime;
357: localtime_r(calendar, &loctime);
358: #else
359: struct tm *loctime = localtime(calendar);
360: #endif /* HT_REENTRANT */
361: sprintf(buf,"%s, %02d %s 19%02d %02d:%02d:%02d",
362: wkdays[gmt->tm_wday],
363: gmt->tm_mday,
364: months[gmt->tm_mon],
365: gmt->tm_year % 100,
366: gmt->tm_hour,
367: gmt->tm_min,
368: gmt->tm_sec);
369: } else {
370: #if defined(HT_REENTRANT) || defined(SOLARIS)
371: struct tm gmt;
372: gmtime_r(calendar, &gmt);
373: #else
374: struct tm *gmt = gmtime(calendar);
375: #endif
376: sprintf(buf,"%s, %02d %s 19%02d %02d:%02d:%02d GMT",
377: wkdays[gmt->tm_wday],
378: gmt->tm_mday,
379: months[gmt->tm_mon],
380: gmt->tm_year % 100,
381: gmt->tm_hour,
382: gmt->tm_min,
383: gmt->tm_sec);
384: }
385: #endif
386: return buf;
387: }
388:
389: /* HTDateDirStr
390: ** ------------
391: ** Generates a date string used in directory listings
392: */
393: PUBLIC BOOL HTDateDirStr (time_t * time, char * str, int len)
394: {
2.8 frystyk 395: #ifdef HAVE_STRFTIME
2.1 frystyk 396: #if defined(HT_REENTRANT) || defined(SOLARIS)
397: struct tm loctime;
398: localtime_r(time, &loctime);
399: strftime(str, len, "%d-%b-%y %H:%M", &loctime);
400: return YES;
401: #else
402: strftime(str, len, "%d-%b-%y %H:%M", localtime(time));
403: return YES;
404: #endif /* HT_REENTRANT || SOLARIS */
405: #else
406: if (len >= 16) {
407: struct tm *loctime = localtime(time);
408: sprintf(bodyptr,"%02d-%s-%02d %02d:%02d",
409: loctime->tm_mday,
410: months[loctime->tm_mon],
411: loctime->tm_year % 100,
412: loctime->tm_hour,
413: loctime->tm_min);
414: return YES;
415: }
416: return NO;
2.8 frystyk 417: #endif /* HAVE_STRFTIME */
2.1 frystyk 418: }
419:
420: /* HTNumToStr
421: ** Converts a long (byte count) to a string
422: ** ----------------------------------------
423: ** This function was a PAIN! In computer-world 1K is 1024 bytes
424: ** and 1M is 1024K -- however, sprintf() still formats in base-10.
425: ** Therefore I output only until 999, and then start using the
426: ** next unit. This doesn't work wrong, it's just a feature.
427: ** The "str" must be large enough to contain the result.
428: */
429: PUBLIC void HTNumToStr (unsigned long n, char * str, int len)
430: {
431: double size = n/1024.0;
432: if (len < 6) {
433: *str = '\0';
434: return;
435: }
436: if (n < 1000)
437: sprintf(str, "%dK", n>0 ? 1 : 0);
438: else if (size + 0.999 < 1000)
439: sprintf(str, "%dK", (int)(size + 0.5));
440: else if ((size /= 1024) < 9.9)
441: sprintf(str, "%.1fM", (size + 0.05));
442: else if (size < 1000)
443: sprintf(str, "%dM", (int)(size + 0.5));
444: else if ((size /= 1024) < 9.9)
445: sprintf(str, "%.1fG", (size + 0.05));
446: else
447: sprintf(str, "%dG", (int)(size + 0.5));
448: }
449:
450: /* Convert file URLs into a local representation
451: ** ---------------------------------------------
452: ** The URL has already been translated through the rules in get_physical
453: ** in HTAccess.c and all we need to do now is to map the path to a local
454: ** representation, for example if must translate '/' to the ones that
455: ** turn the wrong way ;-)
456: ** Returns:
457: ** OK: local file (that must be freed by caller)
458: ** Error: NULL
459: */
2.8 frystyk 460: PUBLIC char * HTWWWToLocal (const char * url, const char * base)
2.1 frystyk 461: {
462: if (url) {
463: char * access = HTParse(url, base, PARSE_ACCESS);
464: char * host = HTParse(url, base, PARSE_HOST);
465: char * path = HTParse(url, base, PARSE_PATH+PARSE_PUNCTUATION);
2.8 frystyk 466: const char *myhost = HTGetHostName();
2.1 frystyk 467:
468: /* Find out if this is a reference to the local file system */
469: if ((*access && strcmp(access, "file")) ||
470: (*host && strcasecomp(host, "localhost") &&
471: myhost && strcmp(host, myhost))) {
472: if (PROT_TRACE)
2.6 eric 473: HTTrace("LocalName... Not on local file system\n");
2.5 frystyk 474: HT_FREE(access);
475: HT_FREE(host);
476: HT_FREE(path);
2.1 frystyk 477: return NULL;
478: } else {
479: char *ptr;
480: if ((ptr = strchr(path, ';')) || (ptr = strchr(path, '?')))
481: *ptr = '\0';
482:
483: /*
484: ** Do whatever translation is required here in order to fit your
485: ** platform _before_ the path is unescaped.
486: */
487: #ifdef VMS
488: HTVMS_checkDecnet(path);
489: #endif
2.3 frystyk 490: #ifdef WWW_MSWINDOWS
2.1 frystyk 491: /* an absolute pathname with logical drive */
492: if (*path == '/' && path[2] == ':')
493: /* NB. need memmove because overlaps */
494: memmove( path, path+1, strlen(path) + 1);
495: #endif
496:
497: HTUnEscape(path); /* Take out the escaped characters */
498: if (PROT_TRACE)
2.6 eric 499: HTTrace("Node........ `%s' means path `%s'\n",url,path);
2.5 frystyk 500: HT_FREE(access);
501: HT_FREE(host);
2.1 frystyk 502: return path;
503: }
504: }
505: return NULL;
506: }
507:
508: /* Convert a local file name into a URL
509: ** ------------------------------------
510: ** Generates a WWW URL name from a local file name or NULL if error.
511: ** Returns:
512: ** OK: local file (that must be freed by caller)
513: ** Error: NULL
514: */
2.8 frystyk 515: PUBLIC char * HTLocalToWWW (const char * local)
2.1 frystyk 516: {
517: char * result = NULL;
518: if (local && *local) {
519: StrAllocCopy(result, "file:"); /* We get an absolute file name */
520: #ifdef VMS
521: /* convert directory name to Unix-style syntax */
522: {
523: char * disk = strchr (local, ':');
524: char * dir = strchr (local, '[');
525: if (disk) {
526: *disk = '\0';
527: StrAllocCat(result, "/"); /* needs delimiter */
528: StrAllocCat(result, local);
529: }
530: if (dir) {
531: char *p;
532: *dir = '/'; /* Convert leading '[' */
533: for (p = dir ; *p != ']'; ++p)
534: if (*p == '.') *p = '/';
535: *p = '\0'; /* Cut on final ']' */
536: StrAllocCat(result, dir);
537: }
538: }
539: #else /* not VMS */
540: #ifdef WIN32
541: {
2.2 frystyk 542: char * p;
2.1 frystyk 543: StrAllocCat(result, "/");
2.2 frystyk 544: StrAllocCat(result, local);
545: p = result;
546: while (*p) {
2.1 frystyk 547: if (*p == '\\') /* change to one true slash */
2.2 frystyk 548: *p = '/';
2.1 frystyk 549: p++;
550: }
551: }
552: #else /* not WIN32 */
553: StrAllocCat (result, local);
554: #endif /* not WIN32 */
555: #endif /* not VMS */
556: }
557: return result;
558: }
Webmaster