1 /*								       HTInet.c
2 **	GENERIC INTERNET UTILITIES
3 **
4 **	(c) COPYRIGHT MIT 1995.
5 **	Please first read the full copyright statement in the file COPYRIGH.
6 **	@(#) $Id$
7 **
8 **	This code is in common between client and server sides.
9 **
10 **	16 Mar 96  HFN	Spawned off from HTTCP.c
11 */
12 
13 /* Library include files */
14 #include "wwwsys.h"
15 #include "WWWUtil.h"
16 #include "HTParse.h"
17 #include "HTAlert.h"
18 #include "HTError.h"
19 #include "HTNetMan.h"
20 #include "HTDNS.h"
21 #include "HTInet.h"					 /* Implemented here */
22 
23 #ifndef DEFAULT_NEWS_HOST
24 #define DEFAULT_NEWS_HOST	"news"
25 #endif
26 
27 #ifndef SERVER_FILE
28 #define SERVER_FILE		"/usr/local/lib/rn/server"
29 #endif
30 
31 /* ------------------------------------------------------------------------- */
32 
33 /*
34 **	Returns the string equivalent to the errno passed in the argument.
35 **	We can't use errno directly as we have both errno and socerrno. The
36 **	result is a dynamic string that must be freed by the caller.
37 */
HTErrnoString(int errornumber)38 PUBLIC char * HTErrnoString (int errornumber)
39 {
40     char * msg = NULL;
41 #ifdef _WINSOCKAPI_
42     if ((msg = (char *) HT_MALLOC(64)) == NULL)
43 	HT_OUTOFMEM("HTErrnoString");
44     *msg = '\0';
45     sprintf(msg, "WinSock reported error=%ld", WSAGetLastError());
46 #else
47 #ifdef HAVE_STRERROR
48     StrAllocCopy(msg, strerror(errornumber));
49 #else
50 #ifdef HAVE_SYS_ERRLIST
51 #ifdef HAVE_SYS_NERR
52     if (errno < sys_nerr)
53 	StrAllocCopy(msg, sys_errlist[errno]);
54     else
55         StrAllocCopy(msg, "Unknown error");
56 #else
57     StrAllocCopy(msg, sys_errlist[errno]);
58 #endif /* HAVE_SYS_NERR */
59 #else
60 #ifdef VMS
61     if ((msg = (char *) HT_MALLOC(64)) == NULL)
62 	HT_OUTOFMEM("HTErrnoString");
63     *msg = '\0';
64     sprintf(msg, "Unix errno=%ld dec, VMS error=%lx hex", errornumber,
65 	    vaxc$errno);
66 #else
67     StrAllocCopy(msg, "Error number not translated!");
68 #endif /* _WINSOCKAPI_ */
69 #endif /* VMS */
70 #endif /* HAVE_SYS_ERRLIST */
71 #endif /* HAVE_STRERROR */
72     return msg;
73 }
74 
75 
76 /*	Debug error message
77 */
HTInetStatus(int errnum,char * where)78 PUBLIC int HTInetStatus (int errnum, char * where)
79 {
80 #ifdef VMS
81     HTTRACE(CORE_TRACE, "System Error Unix = %ld dec\n" _ errno);
82     HTTRACE(CORE_TRACE, "System Error VMS  = %lx hex\n" _ vaxc$errno);
83     return (-vaxc$errno);
84 #else
85 #ifdef _WINSOCKAPI_
86     HTTRACE(CORE_TRACE, "System Error Unix = %ld dec\n" _ errno);
87     HTTRACE(CORE_TRACE, "System Error WinSock error=%lx hex\n" _
88 			    WSAGetLastError());
89     return (-errnum);
90 #else
91 #ifdef HTDEBUG
92     if (CORE_TRACE) {
93 	char * errmsg = HTErrnoString(errnum);
94 	HTTRACE(CORE_TRACE, "System Error %d after call to %s() failed\n............ %s\n" _
95 		errno _ where _ errmsg);
96 	HT_FREE(errmsg);
97     }
98 #endif /* HTDEBUG */
99     return (-errnum);
100 #endif /* _WINSOCKAPI_ */
101 #endif /* VMS */
102 }
103 
104 
105 /*	Parse a cardinal value				       parse_cardinal()
106 **	----------------------
107 **
108 ** On entry,
109 **	*pp	    points to first character to be interpreted, terminated by
110 **		    non 0:9 character.
111 **	*pstatus    points to status already valid
112 **	maxvalue    gives the largest allowable value.
113 **
114 ** On exit,
115 **	*pp	    points to first unread character
116 **	*pstatus    points to status updated iff bad
117 */
118 
HTCardinal(int * pstatus,char ** pp,unsigned int max_value)119 PUBLIC unsigned int HTCardinal (int *		pstatus,
120 				char **		pp,
121 				unsigned int	max_value)
122 {
123     unsigned int n=0;
124     if ( (**pp<'0') || (**pp>'9')) {	    /* Null string is error */
125 	*pstatus = -3;  /* No number where one expeceted */
126 	return 0;
127     }
128     while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0';
129 
130     if (n>max_value) {
131 	*pstatus = -4;  /* Cardinal outside range */
132 	return 0;
133     }
134 
135     return n;
136 }
137 
138 /* ------------------------------------------------------------------------- */
139 /*	       		        SIGNAL HANDLING 			     */
140 /* ------------------------------------------------------------------------- */
141 
142 #ifdef WWWLIB_SIG
143 /*								    HTSetSignal
144 **  This function sets up signal handlers. This might not be necessary to
145 **  call if the application has its own handlers.
146 */
147 #include <signal.h>
148 
HTSetSignal(void)149 PUBLIC void HTSetSignal (void)
150 {
151     /* On some systems (SYSV) it is necessary to catch the SIGPIPE signal
152     ** when attemting to connect to a remote host where you normally should
153     ** get `connection refused' back
154     */
155     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
156 	HTTRACE(CORE_TRACE, "HTSignal.... Can't catch SIGPIPE\n");
157     } else {
158 	HTTRACE(CORE_TRACE, "HTSignal.... Ignoring SIGPIPE\n");
159     }
160 }
161 #else /* WWWLIB_SIG */
162 
HTSetSignal(void)163 PUBLIC void HTSetSignal (void) { }
164 
165 #endif /* WWWLIB_SIG */
166 
167 /* ------------------------------------------------------------------------- */
168 /*	       		     HOST NAME FUNCTIONS 			     */
169 /* ------------------------------------------------------------------------- */
170 
171 /*	Produce a string for an Internet address
172 **	----------------------------------------
173 **
174 ** On exit,
175 **	returns	a pointer to a static string which must be copied if
176 **		it is to be kept.
177 */
HTInetString(SockA * sin)178 PUBLIC const char * HTInetString (SockA * sin)
179 {
180 #ifndef DECNET  /* Function only used below for a trace message */
181 #if 0
182     /* This dumps core on some Sun systems :-(. The problem is now, that
183        the current implememtation only works for IP-addresses and not in
184        other address spaces. */
185     return inet_ntoa(sin->sin_addr);
186 #endif
187     static char string[16];
188     sprintf(string, "%d.%d.%d.%d",
189 	    (int)*((unsigned char *)(&sin->sin_addr)+0),
190 	    (int)*((unsigned char *)(&sin->sin_addr)+1),
191 	    (int)*((unsigned char *)(&sin->sin_addr)+2),
192 	    (int)*((unsigned char *)(&sin->sin_addr)+3));
193     return string;
194 #else
195     return "";
196 #endif /* Decnet */
197 }
198 
199 /*	Parse a network node address and port
200 **	-------------------------------------
201 ** 	It is assumed that any portnumber and numeric host address
202 **	is given in decimal notation. Separation character is '.'
203 **	Any port number gets chopped off
204 **      Returns:
205 **	       	>0	Number of homes
206 **		 0	Wait for persistent socket
207 **		-1	Error
208 */
HTParseInet(HTHost * host,char * hostname,HTRequest * request)209 PUBLIC int HTParseInet (HTHost * host, char * hostname, HTRequest * request)
210 {
211     int status = 1;
212     SockA *sin = &host->sock_addr;
213 
214 #ifdef DECNET
215     /* read Decnet node name. @@ Should know about DECnet addresses, but it's
216        probably worth waiting until the Phase transition from IV to V. */
217 
218     sin->sdn_nam.n_len = min(DN_MAXNAML, strlen(hostname));  /* <=6 in phase 4 */
219     strncpy (sin->sdn_nam.n_name, hostname, sin->sdn_nam.n_len + 1);
220 
221     HTTRACE(CORE_TRACE, "DECnet: Parsed address as object number %d on host %.6s...\n" _
222 		sin->sdn_objnum _ hostname);
223 #else /* Internet */
224     {
225 	char *strptr = hostname;
226 	while (*strptr) {
227 	    if (*strptr == ':') {
228 		*strptr = '\0';	   /* Don't want port number in numeric host */
229 		break;
230 	    }
231 	    if (!isdigit((int) *strptr) && *strptr != '.')
232 		break;
233 	    strptr++;
234 	}
235 	if (!*strptr) {
236 #ifdef GUSI
237 	    sin->sin_addr = inet_addr(hostname); 		 /* See netinet/in.h */
238 #else
239 	    sin->sin_addr.s_addr = inet_addr(hostname);	  /* See arpa/inet.h */
240 #endif
241 	} else {
242 	    char * port = strchr(hostname, ':');			/* Chop port */
243 	    if (port) *port = '\0';
244 	    status = HTGetHostByName(host, hostname, request);
245 	}
246 #ifdef HTDEBUG
247 	if (status > 0)
248 	    HTTRACE(CORE_TRACE, "ParseInet... as port %d on %s with %d homes\n" _
249 		    (int) ntohs(sin->sin_port) _ HTInetString(sin) _ status);
250 #endif /* HTDEBUG */
251     }
252 #endif /* Internet vs. Decnet */
253     return status;
254 }
255 
256 
257 #if 0
258 /*								HTGetDomainName
259 **	Returns the current domain name without the local host name.
260 **	The response is pointing to a static area that might be changed
261 **	using HTSetHostName().
262 **
263 **	Returns NULL on error, "" if domain name is not found
264 */
265 PRIVATE char * HTGetDomainName (void)
266 {
267     char * host = HTGetHostName();
268     char * domain;
269     if (host && *host) {
270 	if ((domain = strchr(host, '.')) != NULL)
271 	    return ++domain;
272 	else
273 	    return "";
274     } else
275 	return NULL;
276 }
277 #endif
278 
279 /*								HTGetHostName
280 **	Returns the name of this host. It uses the following algoritm:
281 **
282 **	1) gethostname()
283 **	2) if the hostname doesn't contain any '.' try to read
284 **	   /etc/resolv.conf. If there is no domain line in this file then
285 **	3) Try getdomainname and do as the man pages say for resolv.conf (sun)
286 **	   If there is no domain line in this file, then it is derived
287 **	   from the domain name set by the domainname(1) command, usually
288 **	   by removing the first component. For example, if the domain-
289 **	   name is set to ``foo.podunk.edu'' then the default domain name
290 **	   used will be ``pudunk.edu''.
291 **
292 **	This is the same procedure as used by res_init() and sendmail.
293 **
294 **	Return: hostname on success else NULL
295 */
HTGetHostName(void)296 PUBLIC char * HTGetHostName (void)
297 {
298     char * hostname = NULL;
299     int fqdn = 0;				     /* 0=no, 1=host, 2=fqdn */
300     char name[MAXHOSTNAMELEN+1];
301     *(name+MAXHOSTNAMELEN) = '\0';
302 
303 #if defined(HAVE_SYSINFO) && defined(SI_HOSTNAME)
304     if (!fqdn && sysinfo(SI_HOSTNAME, name, MAXHOSTNAMELEN) > 0) {
305 	char * dot = strchr(name, '.');
306 	HTTRACE(CORE_TRACE, "HostName.... sysinfo says `%s\'\n" _ name);
307 	StrAllocCopy(hostname, name);
308 	fqdn = dot ? 2 : 1;
309     }
310 #endif /* HAVE_SYSINFO */
311 
312 #ifdef HAVE_GETHOSTNAME
313     if (!fqdn && gethostname(name, MAXHOSTNAMELEN) == 0) {
314 	char * dot = strchr(name, '.');
315 	HTTRACE(CORE_TRACE, "HostName.... gethostname says `%s\'\n" _ name);
316 	StrAllocCopy(hostname, name);
317 	fqdn = dot ? 2 : 1;
318     }
319 #endif /* HAVE_GETHOSTNAME */
320 
321 #ifdef RESOLV_CONF
322     /* Now try the resolver config file */
323     {
324 	FILE *fp;
325 	if (fqdn==1 && (fp = fopen(RESOLV_CONF, "r")) != NULL) {
326 	    char buffer[80];
327 	    *(buffer+79) = '\0';
328 	    while (fgets(buffer, 79, fp)) {
329 		if (!strncasecomp(buffer, "domain", 6) ||
330 		    !strncasecomp(buffer, "search", 6)) {
331 		    char *domainstr = buffer+6;
332 		    char *end;
333 		    while (*domainstr == ' ' || *domainstr == '\t')
334 			domainstr++;
335 		    end = domainstr;
336 		    while (*end && !isspace((int) *end))
337 			end++;
338 		    *end = '\0';
339 		    if (*domainstr) {
340 			StrAllocCat(hostname, ".");
341 			StrAllocCat(hostname, domainstr);
342 			fqdn = 2;
343 			break;
344 		    }
345 		}
346 	    }
347 	    fclose(fp);
348 	}
349     }
350 #endif /* RESOLV_CONF */
351 
352 #ifdef HAVE_GETDOMAINNAME
353     /* If everything else has failed then try getdomainname */
354     if (fqdn==1) {
355 	if (getdomainname(name, MAXHOSTNAMELEN)) {
356 	    HTTRACE(CORE_TRACE, "HostName.... Can't get domain name\n");
357 	    StrAllocCopy(hostname, "");
358 	    return NULL;
359 	}
360 
361 	/* If the host name and the first part of the domain name are different
362 	   then use the former as it is more exact (I guess) */
363 	if (strncmp(name, hostname, (int) strlen(hostname))) {
364 	    char *domain = strchr(name, '.');
365 	    if (!domain)
366 		domain = name;
367 	    StrAllocCat(hostname, domain);
368 	}
369     }
370 #endif /* HAVE_GETDOMAINNAME */
371 
372     if (hostname) {
373 	char *strptr = hostname;
374 	while (*strptr) {
375 	    *strptr = TOLOWER(*strptr);
376 	    strptr++;
377 	}
378 	if (*(hostname+strlen(hostname)-1) == '.')    /* Remove trailing dot */
379 	    *(hostname+strlen(hostname)-1) = '\0';
380 	HTTRACE(CORE_TRACE, "HostName.... FQDN is `%s\'\n" _ hostname);
381     }
382     return hostname;
383 }
384 
385 /*							       HTGetMailAddress
386 **
387 **	Get the mail address of the current user on the current host. The
388 **	domain name used is the one initialized in HTSetHostName or
389 **	HTGetHostName. The login name is determined using (ordered):
390 **
391 **		getlogin
392 **		getpwuid(getuid())
393 **
394 **	The weakness about the last attempt is if the user has multiple
395 **	login names each with the same user ID. If this fails as well then:
396 **
397 **		LOGNAME environment variable
398 **		USER environment variable
399 **
400 **	Returns NULL or string to be freed by caller
401 */
HTGetMailAddress(void)402 PUBLIC char * HTGetMailAddress (void)
403 {
404 #ifdef HT_REENTRANT
405   char name[HT_LOGNAME_MAX];    /* For getlogin_r or getUserName */
406   int result;
407 #endif
408 #ifdef WWW_MSWINDOWS/* what was the plan for this under windows? - EGP */
409   char name[256];    /* For getlogin_r or getUserName */
410   unsigned int bufSize = sizeof(name);
411 #endif
412 #ifdef HAVE_PWD_H
413     struct passwd * pw_info = NULL;
414 #endif
415     char * login = NULL;
416 
417 #ifdef WWW_MSWINDOWS
418     if (!login && GetUserName(name, &bufSize) != TRUE)
419         HTTRACE(CORE_TRACE, "MailAddress. GetUsername returns NO\n");
420 #endif /* WWW_MSWINDOWS */
421 
422 #ifdef HAVE_CUSERID
423     if (!login && (login = (char *) cuserid(NULL)) == NULL)
424         HTTRACE(CORE_TRACE, "MailAddress. cuserid returns NULL\n");
425 #endif /* HAVE_CUSERID */
426 
427 #ifdef HAVE_GETLOGIN
428 #ifdef GETLOGIN_R_RETURNS_POINTER
429     if (!login && (login = (char *) getlogin_r(name, HT_LOGNAME_MAX)) == NULL)
430 #elif defined(GETLOGIN_R_RETURNS_INT)
431     if (!login && (result = getlogin_r(name, HT_LOGNAME_MAX)) == 0)
432     {
433 	login = &name[0];
434     }
435     else
436 #else
437     if (!login && (login = (char *) getlogin()) == NULL)
438 #endif /* HT_REENTRANT */
439 	HTTRACE(CORE_TRACE, "MailAddress. getlogin returns NULL\n");
440 #endif /* HAVE_GETLOGIN */
441 
442 #ifdef HAVE_PWD_H
443     if (!login && (pw_info = getpwuid(getuid())) != NULL)
444 	login = pw_info->pw_name;
445 #endif /* HAVE_PWD_H */
446 
447     if (!login && (login = getenv("LOGNAME")) == NULL)
448 	HTTRACE(CORE_TRACE, "MailAddress. LOGNAME not found\n");
449 
450     if (!login && (login = getenv("USER")) == NULL)
451 	HTTRACE(CORE_TRACE, "MailAddress. USER not found\n");
452 
453     if (!login) login = HT_DEFAULT_LOGIN;
454 
455     if (login) {
456 	char * domain = NULL;
457 	char * mailaddress = NULL;
458 	StrAllocCopy(mailaddress, login);
459 	StrAllocCat(mailaddress, "@");
460 	if ((domain = HTGetHostName()) != NULL) {
461 	    StrAllocCat(mailaddress, domain);
462 	    HT_FREE(domain);
463 	}
464 	return mailaddress;
465     }
466     return NULL;
467 }
468 
469 /*
470 **	Except on the NeXT, we pick up the NewsHost name from
471 **
472 **	1.	Environment variable NNTPSERVER
473 **	2.	File SERVER_FILE
474 **	3.	Compilation time macro DEFAULT_NEWS_HOST
475 **
476 **	On the NeXT, we pick up the NewsHost name from, in order:
477 **
478 **	1.	WorldWideWeb default "NewsHost"
479 **	2.	News default "NewsHost"
480 **	3.	Compilation time macro DEFAULT_NEWS_HOST
481 **
482 **	Returns NULL or string to be freed by caller
483 */
HTGetNewsServer(void)484 PUBLIC char * HTGetNewsServer (void)
485 {
486     char * newshost = NULL;
487     char buffer[80];
488 
489 #ifdef NeXTStep
490     if ((newshost = NXGetDefaultValue("WorldWideWeb","NewsHost")) == 0)
491 	if ((newshost = NXGetDefaultValue("News","NewsHost")) == 0)
492 	    newshost = DEFAULT_NEWS_HOST;
493 #else
494     if ((newshost = (char *) getenv("NNTPSERVER")) == NULL) {
495 	FILE *fp = fopen(SERVER_FILE, "r");
496 	*(buffer+79) = '\0';
497 	if (fp) {
498 	    if (fgets(buffer, 79, fp)) {
499 		char *end;
500 		newshost = buffer;
501 		while (*newshost == ' ' || *newshost == '\t')
502 		    newshost++;
503 		end = newshost;
504 		while (*end && !isspace((int) *end))
505 		    end++;
506 		*end = '\0';
507 	    }
508 	    fclose(fp);
509 	}
510     }
511 #endif /* NestStep */
512 
513     /* Last resort */
514     if (!newshost || !*newshost) newshost = DEFAULT_NEWS_HOST;
515 
516     /* Canonicalize host name */
517     {
518 	char * result = NULL;
519 	StrAllocCopy(result, newshost);
520 	{
521 	    char * strptr = result;
522 	    while (*strptr) {
523 		*strptr = TOLOWER(*strptr);
524 		strptr++;
525 	    }
526 	}
527 	return result;
528     }
529 }
530 
531 /*	Timezone Offset
532 **	---------------
533 **	Calculates the offset from GMT in seconds
534 */
HTGetTimeZoneOffset(void)535 PUBLIC time_t HTGetTimeZoneOffset (void)
536 {
537     static time_t HTTimeZone = -1;		  /* Invalid timezone offset */
538     if (HTTimeZone != -1) return HTTimeZone;		     /* Already done */
539 #ifdef HAVE_TIMEZONE
540     {
541 	time_t cur_t = time(NULL);
542 #ifdef HT_REENTRANT
543 	struct tm loctime;
544 	struct tm *local = (struct tm *) localtime_r(&cur_t, &loctime);
545 #else
546 	struct tm *local = localtime(&cur_t);
547 #endif /* HT_REENTRANT */
548 #ifdef HAVE_DAYLIGHT
549 	if (daylight && local->tm_isdst>0) {		   /* daylight time? */
550 #else
551 	if (local->tm_isdst>0) {			   /* daylight time? */
552 #endif /* HAVE_DAYLIGHT */
553 #ifdef HAVE_ALTZONE
554 	    HTTimeZone = altzone;
555 #else
556  	    /* Assumes a fixed DST offset of 1 hour, which is probably wrong */
557 #ifdef __CYGWIN__
558  	    HTTimeZone = _timezone - 3600;
559 #else
560  	    HTTimeZone = timezone - 3600;
561 #endif
562 #endif /* HAVE_ALTZONE */
563 	} else {						       /* no */
564 #ifdef __CYGWIN__
565 	    HTTimeZone = _timezone;
566 #else
567 	    HTTimeZone = timezone;
568 #endif
569 	}
570 	HTTimeZone = -HTTimeZone;
571 	HTTRACE(CORE_TRACE, "TimeZone.... GMT + (%02d) hours (including DST)\n" _
572 		    (int) HTTimeZone/3600);
573     }
574 #else
575 #ifdef HAVE_TM_GMTOFF
576     {
577 	time_t cur_t = time(NULL);
578 #ifdef HT_REENTRANT
579 	struct tm loctime;
580 	localtime_r(&cur_t, &loctime);
581 #else
582 	struct tm * local = localtime(&cur_t);
583 #endif /* HT_REENTRANT */
584 	HTTimeZone = local->tm_gmtoff;
585 	HTTRACE(CORE_TRACE, "TimeZone.... GMT + (%02d) hours (including DST)\n" _
586 		    (int)local->tm_gmtoff / 3600);
587     }
588 #else
589     HTTRACE(CORE_TRACE, "TimeZone.... Not defined\n");
590 #endif /* HAVE_TM_GMTOFF */
591 #endif /* HAVE_TIMEZONE */
592     return HTTimeZone;
593 }
594 
595 /*
596 **	Finds a temporary name in in the directory given. If the directory
597 **	is NULL then don't prepend anything.
598 **	If success, the result must be freed by caller, else we return NULL
599 */
600 PUBLIC char * HTGetTmpFileName (const char * abs_dir)
601 {
602     char * result = NULL;
603 #ifdef HAVE_TEMPNAM
604     static char * envtmpdir = NULL;
605     size_t len = 0;
606     if (abs_dir && *abs_dir) {
607       char * tmpdir = getenv("TMPDIR");
608       if (tmpdir)
609           len = strlen(tmpdir);
610       if (len) {
611           if (!(envtmpdir = (char *) HT_REALLOC(envtmpdir, len + 8)))
612               HT_OUTOFMEM("HTGetTmpFileName");
613           strcpy(envtmpdir, "TMPDIR=");
614           strcpy(envtmpdir + 7, tmpdir);
615           putenv("TMPDIR=");
616       }
617     }
618 #ifdef __CYGWIN__
619     result = tempnam(abs_dir, "");
620 #else
621     result = tempnam(abs_dir, NULL);
622 #endif /* __CYGWIN__ */
623     if (len)
624       putenv(envtmpdir);
625 #else
626     /*
627     **  This is only approx. as we don't know if this file exists or not.
628     **  Hopefully, tempnam() exists on enough platforms so that this is not
629     **  a problem.
630     */
631     char * offset = NULL;
632     if (!(result = (char *) HT_MALLOC((abs_dir ? strlen(abs_dir) : 0) +
633 				      HT_MAX_TMPNAM + 2)))
634 	HT_OUTOFMEM("HTGetTmpFileName");
635 
636 #ifdef WWW_MSWINDOWS
637     if (abs_dir) {
638 #else
639     if (abs_dir && *abs_dir==DIR_SEPARATOR_CHAR) {
640 #endif /* WWW_MSWINDOWS */
641 	strcpy(result, abs_dir);
642 	offset = result+strlen(result);
643 	if (*(offset-1) != DIR_SEPARATOR_CHAR) *offset++ = DIR_SEPARATOR_CHAR;
644 
645 #ifdef HT_REENTRANT
646 	tmpnam_r(offset);
647 #else
648 	tmpnam(offset);
649 #endif
650 
651 	{
652 	    char * orig = strrchr(offset, DIR_SEPARATOR_CHAR);
653 	    char * dest = offset;
654 	    if (orig++) while ((*dest++ = *orig++));
655 	}
656     } else {
657 	offset = result;
658 #ifdef HT_REENTRANT
659 	tmpnam_r(offset);
660 #else
661 	tmpnam(offset);
662 #endif
663 	offset = result;
664     }
665 #endif /* HAVE_TEMPNAM */
666     return result;
667 }
668 
669 /*
670 **  Copied from X utilities
671 */
672 PUBLIC ms_t HTGetTimeInMillis (void)
673 {
674 #ifdef WWW_MSWINDOWS
675     return GetTickCount();
676 #else /* WWW_MSWINDOWS */
677 #ifdef HAVE_GETTIMEOFDAY
678     struct timeval tp;
679     gettimeofday(&tp, NULL);
680     return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
681 #else
682     return((ms_t) 0);
683 #endif
684 #endif /* !WWW_MSWINDOWS */
685 }
686