Annotation of libwww/Library/src/HTInet.c, revision 2.4

2.1       frystyk     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.
2.4     ! frystyk     6: **     @(#) $Id: HTInet.c,v 2.3 1996/05/20 15:06:49 frystyk Exp $
2.1       frystyk     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 "sysdep.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: 
2.3       frystyk    23: #ifndef DEFAULT_NEWS_HOST
                     24: #define DEFAULT_NEWS_HOST      "news"
                     25: #endif
2.1       frystyk    26: 
2.3       frystyk    27: #ifndef SERVER_FILE
                     28: #define SERVER_FILE            "/usr/local/lib/rn/server"
                     29: #endif
2.1       frystyk    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 static buffer.
                     37: */
                     38: PUBLIC const char * HTErrnoString (int errornumber)
                     39: {
                     40: #ifdef HAVE_STRERROR
                     41:     return strerror(errornumber);
                     42: #else
                     43: #ifdef HAVE_SYS_ERRLIST
                     44: #ifdef HAVE_SYS_NERR
                     45:     return (errno < sys_nerr ? sys_errlist[errno] : "Unknown error");
                     46: #else
                     47:     return sys_errlist[errno];
                     48: #endif /* HAVE_SYS_NERR */
                     49: #else
                     50: #ifdef VMS
                     51:     static char buf[60];
                     52:     sprintf(buf, "Unix errno=%ld dec, VMS error=%lx hex", errornumber,
                     53:            vaxc$errno);
                     54:     return buf;
                     55: #else
                     56: #ifdef _WINSOCKAPI_
                     57:     static char buf[60];
                     58:     sprintf(buf, "Unix errno=%ld dec, WinSock error=%ld", errornumber,
                     59:            WSAGetLastError());
                     60:     return buf;
                     61: #else
                     62:     return "(Error number not translated)";
                     63: #endif /* _WINSOCKAPI_ */
                     64: #endif /* VMS */
                     65: #endif /* HAVE_SYS_ERRLIST */
                     66: #endif /* HAVE_STRERROR */
                     67: }
                     68: 
                     69: 
                     70: /*     Debug error message
                     71: */
                     72: PUBLIC int HTInetStatus (int errnum, char * where)
                     73: {
                     74: #ifdef VMS
                     75:     if (PROT_TRACE) HTTrace("System Error Unix = %ld dec\n", errno);
                     76:     if (PROT_TRACE) HTTrace("System Error VMS  = %lx hex\n", vaxc$errno);
                     77:     return (-vaxc$errno);
                     78: #else
                     79: #ifdef _WINSOCKAPI_
                     80:     if (PROT_TRACE) HTTrace("System Error Unix = %ld dec\n", errno);
                     81:     if (PROT_TRACE) HTTrace("System Error WinSock error=%lx hex\n",
                     82:                            WSAGetLastError());
                     83:     return (-errnum);
                     84: #else
                     85:     if (PROT_TRACE)
                     86:        HTTrace("System Error %d after call to %s() failed\n............ %s\n",
                     87:                errno, where, HTErrnoString(errnum));
                     88:     return (-errnum);
                     89: #endif /* _WINSOCKAPI_ */
                     90: #endif /* VMS */
                     91: }
                     92: 
                     93: 
                     94: /*     Parse a cardinal value                                 parse_cardinal()
                     95: **     ----------------------
                     96: **
                     97: ** On entry,
                     98: **     *pp         points to first character to be interpreted, terminated by
                     99: **                 non 0:9 character.
                    100: **     *pstatus    points to status already valid
                    101: **     maxvalue    gives the largest allowable value.
                    102: **
                    103: ** On exit,
                    104: **     *pp         points to first unread character
                    105: **     *pstatus    points to status updated iff bad
                    106: */
                    107: 
                    108: PUBLIC unsigned int HTCardinal (int *          pstatus,
                    109:                                char **         pp,
                    110:                                unsigned int    max_value)
                    111: {
                    112:     unsigned int n=0;
                    113:     if ( (**pp<'0') || (**pp>'9')) {       /* Null string is error */
                    114:        *pstatus = -3;  /* No number where one expeceted */
                    115:        return 0;
                    116:     }
                    117:     while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0';
                    118: 
                    119:     if (n>max_value) {
                    120:        *pstatus = -4;  /* Cardinal outside range */
                    121:        return 0;
                    122:     }
                    123: 
                    124:     return n;
                    125: }
                    126: 
                    127: /* ------------------------------------------------------------------------- */
                    128: /*                             SIGNAL HANDLING                              */
                    129: /* ------------------------------------------------------------------------- */
                    130: 
                    131: #ifdef WWWLIB_SIG
                    132: /*                                                                 HTSetSignal
                    133: **  This function sets up signal handlers. This might not be necessary to
                    134: **  call if the application has its own handlers.
                    135: */
                    136: #include <signal.h>
                    137: PUBLIC void HTSetSignal (void)
                    138: {
                    139:     /* On some systems (SYSV) it is necessary to catch the SIGPIPE signal
                    140:     ** when attemting to connect to a remote host where you normally should
                    141:     ** get `connection refused' back
                    142:     */
                    143:     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
                    144:        if (PROT_TRACE) HTTrace("HTSignal.... Can't catch SIGPIPE\n");
                    145:     } else {
                    146:        if (PROT_TRACE) HTTrace("HTSignal.... Ignoring SIGPIPE\n");
                    147:     }
                    148: }
                    149: #else
                    150: #ifdef WWW_WIN_DLL
                    151: PUBLIC void HTSetSignal (void) {}
                    152: #endif /* WWW_WIN_DLL */
                    153: #endif /* WWWLIB_SIG */
                    154: 
                    155: /* ------------------------------------------------------------------------- */
                    156: /*                          HOST NAME FUNCTIONS                             */
                    157: /* ------------------------------------------------------------------------- */
                    158: 
                    159: /*     Produce a string for an Internet address
                    160: **     ----------------------------------------
                    161: **
                    162: ** On exit,
                    163: **     returns a pointer to a static string which must be copied if
                    164: **             it is to be kept.
                    165: */
                    166: PUBLIC const char * HTInetString (SockA * sin)
                    167: {
                    168: #ifndef DECNET  /* Function only used below for a trace message */
                    169: #if 0
                    170:     /* This dumps core on some Sun systems :-(. The problem is now, that 
                    171:        the current implememtation only works for IP-addresses and not in
                    172:        other address spaces. */
                    173:     return inet_ntoa(sin->sin_addr);
                    174: #endif
                    175:     static char string[16];
                    176:     sprintf(string, "%d.%d.%d.%d",
                    177:            (int)*((unsigned char *)(&sin->sin_addr)+0),
                    178:            (int)*((unsigned char *)(&sin->sin_addr)+1),
                    179:            (int)*((unsigned char *)(&sin->sin_addr)+2),
                    180:            (int)*((unsigned char *)(&sin->sin_addr)+3));
                    181:     return string;
                    182: #else
                    183:     return "";
                    184: #endif /* Decnet */
                    185: }
                    186: 
                    187: /*     Parse a network node address and port
                    188: **     -------------------------------------
                    189: **     It is assumed that any portnumber and numeric host address
                    190: **     is given in decimal notation. Separation character is '.'
2.2       frystyk   191: **     Any port number gets chopped off
2.1       frystyk   192: **      Returns:
                    193: **             >0      Number of homes
                    194: **              0      Wait for persistent socket
                    195: **             -1      Error
                    196: */
                    197: PUBLIC int HTParseInet (HTNet * net, char * host)
                    198: {
                    199:     int status = 1;
                    200:     SockA *sin = &net->sock_addr;
                    201: 
                    202: #ifdef DECNET
                    203:     /* read Decnet node name. @@ Should know about DECnet addresses, but it's
                    204:        probably worth waiting until the Phase transition from IV to V. */
                    205: 
                    206:     sin->sdn_nam.n_len = min(DN_MAXNAML, strlen(host));  /* <=6 in phase 4 */
                    207:     strncpy (sin->sdn_nam.n_name, host, sin->sdn_nam.n_len + 1);
                    208: 
                    209:     if (PROT_TRACE)
                    210:        HTTrace("DECnet: Parsed address as object number %d on host %.6s...\n",
                    211:                sin->sdn_objnum, host);
                    212: #else /* Internet */
                    213:     {
                    214:        char *strptr = host;
                    215:        while (*strptr) {
                    216:            if (*strptr == ':') {
                    217:                *strptr = '\0';    /* Don't want port number in numeric host */
                    218:                break;
                    219:            }
                    220:            if (!isdigit(*strptr) && *strptr != '.')
                    221:                break;
                    222:            strptr++;
                    223:        }
                    224:        if (!*strptr) {
                    225: #ifdef GUSI
                    226:            sin->sin_addr = inet_addr(host);             /* See netinet/in.h */
                    227: #else
                    228:            sin->sin_addr.s_addr = inet_addr(host);       /* See arpa/inet.h */
                    229: #endif
2.2       frystyk   230:        } else {
                    231:            char * port = strchr(host, ':');                    /* Chop port */
                    232:            if (port) *port = '\0';
2.1       frystyk   233:            status = HTGetHostByName(net, host);
2.2       frystyk   234:        }
2.1       frystyk   235:        if (PROT_TRACE) {
                    236:            if (status > 0)
                    237:                HTTrace("ParseInet... as port %d on %s with %d homes\n",
                    238:                        (int) ntohs(sin->sin_port), HTInetString(sin), status);
                    239:        }
                    240:     }
                    241: #endif /* Internet vs. Decnet */
                    242:     return status;
                    243: }
                    244: 
                    245: 
2.3       frystyk   246: #if 0
2.1       frystyk   247: /*                                                             HTGetDomainName
                    248: **     Returns the current domain name without the local host name.
                    249: **     The response is pointing to a static area that might be changed
                    250: **     using HTSetHostName().
                    251: **
                    252: **     Returns NULL on error, "" if domain name is not found
                    253: */
2.3       frystyk   254: PRIVATE char * HTGetDomainName (void)
2.1       frystyk   255: {
2.3       frystyk   256:     char * host = HTGetHostName();
                    257:     char * domain;
2.1       frystyk   258:     if (host && *host) {
                    259:        if ((domain = strchr(host, '.')) != NULL)
                    260:            return ++domain;
                    261:        else
                    262:            return "";
                    263:     } else
                    264:        return NULL;
                    265: }
2.3       frystyk   266: #endif
2.1       frystyk   267: 
                    268: /*                                                             HTGetHostName
                    269: **     Returns the name of this host. It uses the following algoritm:
                    270: **
                    271: **     1) gethostname()
                    272: **     2) if the hostname doesn't contain any '.' try to read
                    273: **        /etc/resolv.conf. If there is no domain line in this file then
                    274: **     3) Try getdomainname and do as the man pages say for resolv.conf (sun)
                    275: **        If there is no domain line in this file, then it is derived
                    276: **        from the domain name set by the domainname(1) command, usually
                    277: **        by removing the first component. For example, if the domain-
                    278: **        name is set to ``foo.podunk.edu'' then the default domain name
                    279: **        used will be ``pudunk.edu''.
                    280: **
                    281: **     This is the same procedure as used by res_init() and sendmail.
                    282: **
                    283: **     Return: hostname on success else NULL
                    284: */
2.3       frystyk   285: PUBLIC char * HTGetHostName (void)
2.1       frystyk   286: {
2.3       frystyk   287:     char * hostname = NULL;
2.1       frystyk   288:     int fqdn = 0;                                   /* 0=no, 1=host, 2=fqdn */
                    289:     FILE *fp;
                    290:     char name[MAXHOSTNAMELEN+1];
                    291:     *(name+MAXHOSTNAMELEN) = '\0';
                    292: 
                    293: #ifdef HAVE_SYSINFO
                    294:     if (!fqdn && sysinfo(SI_HOSTNAME, name, MAXHOSTNAMELEN) > 0) {
                    295:        char * dot = strchr(name, '.');
                    296:        if (PROT_TRACE) HTTrace("HostName.... sysinfo says `%s\'\n", name);
                    297:        StrAllocCopy(hostname, name);
                    298:        fqdn = dot ? 2 : 1;
                    299:     }
                    300: #endif /* HAVE_SYSINFO */
                    301: 
                    302: #ifdef HAVE_GETHOSTNAME
                    303:     if (!fqdn && gethostname(name, MAXHOSTNAMELEN) == 0) {
                    304:        char * dot = strchr(name, '.');
                    305:        if (PROT_TRACE) HTTrace("HostName.... gethostname says `%s\'\n", name);
                    306:        StrAllocCopy(hostname, name);
                    307:        fqdn = dot ? 2 : 1;
                    308:     }
                    309: #endif /* HAVE_GETHOSTNAME */
                    310: 
                    311: #ifdef RESOLV_CONF
                    312:     /* Now try the resolver config file */
                    313:     if (fqdn==1 && (fp = fopen(RESOLV_CONF, "r")) != NULL) {
                    314:        char buffer[80];
                    315:        *(buffer+79) = '\0';
                    316:        while (fgets(buffer, 79, fp)) {
                    317:            if (!strncasecomp(buffer, "domain", 6) ||
                    318:                !strncasecomp(buffer, "search", 6)) {
                    319:                char *domainstr = buffer+6;
                    320:                char *end;
                    321:                while (*domainstr == ' ' || *domainstr == '\t')
                    322:                    domainstr++;
                    323:                end = domainstr;
                    324:                while (*end && !isspace(*end))
                    325:                    end++;
                    326:                *end = '\0';
                    327:                if (*domainstr) {
                    328:                    StrAllocCat(hostname, ".");
                    329:                    StrAllocCat(hostname, domainstr);
                    330:                    fqdn = YES;
                    331:                    break;
                    332:                }
                    333:            }
                    334:        }
                    335:        fclose(fp);
                    336:     }
                    337: #endif /* RESOLV_CONF */
                    338: 
                    339: #ifdef HAVE_GETDOMAINNAME
                    340:     /* If everything else has failed then try getdomainname */
                    341:     if (fqdn==1) {
                    342:        if (getdomainname(name, MAXHOSTNAMELEN)) {
                    343:            if (PROT_TRACE)
                    344:                HTTrace("HostName.... Can't get domain name\n");
                    345:            StrAllocCopy(hostname, "");
                    346:            return NULL;
                    347:        }
                    348: 
                    349:        /* If the host name and the first part of the domain name are different
                    350:           then use the former as it is more exact (I guess) */
                    351:        if (strncmp(name, hostname, (int) strlen(hostname))) {
                    352:            char *domain = strchr(name, '.');
                    353:            if (!domain)
                    354:                domain = name;
                    355:            StrAllocCat(hostname, domain);
                    356:        }
                    357:     }
                    358: #endif /* HAVE_GETDOMAINNAME */
                    359: 
                    360:     if (hostname) {
                    361:        char *strptr = hostname;
                    362:        while (*strptr) {           
                    363:            *strptr = TOLOWER(*strptr);
                    364:            strptr++;
                    365:        }
                    366:        if (*(hostname+strlen(hostname)-1) == '.')    /* Remove trailing dot */
                    367:            *(hostname+strlen(hostname)-1) = '\0';
                    368:        if (PROT_TRACE) HTTrace("HostName.... FQDN is `%s\'\n", hostname);
                    369:     }
                    370:     return hostname;
                    371: }
                    372: 
                    373: /*                                                            HTGetMailAddress
                    374: **
                    375: **     Get the mail address of the current user on the current host. The
                    376: **     domain name used is the one initialized in HTSetHostName or
                    377: **     HTGetHostName. The login name is determined using (ordered):
                    378: **
                    379: **             getlogin
                    380: **             getpwuid(getuid())
                    381: **
                    382: **     The weakness about the last attempt is if the user has multiple
                    383: **     login names each with the same user ID. If this fails as well then:
                    384: **
                    385: **             LOGNAME environment variable
                    386: **             USER environment variable
                    387: **
2.3       frystyk   388: **     Returns NULL or string to be freed by caller
2.1       frystyk   389: */
2.3       frystyk   390: PUBLIC char * HTGetMailAddress (void)
2.1       frystyk   391: {
                    392: #ifdef HT_REENTRANT
                    393:   char name[LOGNAME_MAX+1];    /* For getlogin_r or getUserName */
                    394: #endif
                    395: #ifdef WWW_MSWINDOWS/* what was the plan for this under windows? - EGP */
                    396:   char name[256];    /* For getlogin_r or getUserName */
                    397:   unsigned int bufSize = sizeof(name);
                    398: #endif
                    399: #ifdef HAVE_PWD_H
                    400:     struct passwd * pw_info = NULL;
                    401: #endif
                    402:     char * login = NULL;
                    403: 
                    404: #ifdef WWW_MSWINDOWS
                    405:     if (!login && GetUserName(name, &bufSize) != TRUE)
                    406:         if (PROT_TRACE) HTTrace("MailAddress. GetUsername returns NO\n");
                    407: #endif /* WWW_MSWINDOWS */
                    408: 
                    409: #ifdef HAVE_CUSERID
                    410:     if (!login && (login = (char *) cuserid(NULL)) == NULL)
                    411:         if (PROT_TRACE) HTTrace("MailAddress. cuserid returns NULL\n");
                    412: #endif /* HAVE_CUSERID */
                    413: 
                    414: #ifdef HAVE_GETLOGIN
                    415: #ifdef HT_REENTRANT
                    416:     if (!login && (login = (char *) getlogin_r(name, LOGNAME_MAX)) == NULL)
                    417: #else
                    418:     if (!login && (login = (char *) getlogin()) == NULL)
                    419: #endif /* HT_REENTRANT */
                    420:        if (PROT_TRACE) HTTrace("MailAddress. getlogin returns NULL\n");
                    421: #endif /* HAVE_GETLOGIN */
                    422: 
                    423: #ifdef HAVE_PWD_H
                    424:     if (!login && (pw_info = getpwuid(getuid())) != NULL)
                    425:        login = pw_info->pw_name;
                    426: #endif /* HAVE_PWD_H */
                    427: 
                    428:     if (!login && (login = getenv("LOGNAME")) == NULL)
                    429:        if (PROT_TRACE) HTTrace("MailAddress. LOGNAME not found\n");
                    430: 
                    431:     if (!login && (login = getenv("USER")) == NULL)
                    432:        if (PROT_TRACE) HTTrace("MailAddress. USER not found\n");
                    433: 
                    434:     if (!login) login = HT_DEFAULT_LOGIN;
                    435: 
                    436:     if (login) {
2.3       frystyk   437:        char * domain = NULL;
                    438:        char * mailaddress = NULL;
2.1       frystyk   439:        StrAllocCopy(mailaddress, login);
                    440:        StrAllocCat(mailaddress, "@");
2.3       frystyk   441:        if ((domain = HTGetHostName()) != NULL) {
2.1       frystyk   442:            StrAllocCat(mailaddress, domain);
2.3       frystyk   443:            HT_FREE(domain);
2.1       frystyk   444:        }
                    445:        return mailaddress;
                    446:     }
                    447:     return NULL;
                    448: }
                    449: 
2.3       frystyk   450: /*
                    451: **     Except on the NeXT, we pick up the NewsHost name from
                    452: **
                    453: **     1.      Environment variable NNTPSERVER
                    454: **     2.      File SERVER_FILE
                    455: **     3.      Compilation time macro DEFAULT_NEWS_HOST
                    456: **
                    457: **     On the NeXT, we pick up the NewsHost name from, in order:
                    458: **
                    459: **     1.      WorldWideWeb default "NewsHost"
                    460: **     2.      News default "NewsHost"
                    461: **     3.      Compilation time macro DEFAULT_NEWS_HOST
                    462: **
                    463: **     Returns NULL or string to be freed by caller
                    464: */
                    465: PUBLIC char * HTGetNewsServer (void)
                    466: {
                    467:     char * newshost = NULL;
                    468:     char buffer[80];
                    469: 
                    470: #ifdef NeXTStep
                    471:     if ((newshost = NXGetDefaultValue("WorldWideWeb","NewsHost")) == 0)
                    472:        if ((newshost = NXGetDefaultValue("News","NewsHost")) == 0)
                    473:            newshost = DEFAULT_NEWS_HOST;
                    474: #else
                    475:     if ((newshost = (char *) getenv("NNTPSERVER")) == NULL) {
                    476:        FILE *fp = fopen(SERVER_FILE, "r");
                    477:        *(buffer+79) = '\0';
                    478:        if (fp) {
                    479:            if (fgets(buffer, 79, fp)) {
                    480:                char *end;
                    481:                newshost = buffer;
                    482:                while (*newshost == ' ' || *newshost == '\t')
                    483:                    newshost++;
                    484:                end = newshost;
                    485:                while (*end && !isspace(*end))
                    486:                    end++;
                    487:                *end = '\0';
                    488:            }
                    489:            fclose(fp);
                    490:        }
                    491:     }
                    492: #endif /* NestStep */
                    493: 
                    494:     /* Last resort */
                    495:     if (!newshost || !*newshost) newshost = DEFAULT_NEWS_HOST;
                    496: 
                    497:     /* Canonicalize host name */
                    498:     {
                    499:        char * result = NULL;
                    500:        StrAllocCopy(result, newshost);
                    501:        {
                    502:            char * strptr = result;
                    503:            while (*strptr) {
                    504:                *strptr = TOLOWER(*strptr);
                    505:                strptr++;
                    506:            }
                    507:        }
                    508:        return result;
                    509:     }
                    510: }
2.1       frystyk   511: 
2.3       frystyk   512: /*     Timezone Offset
                    513: **     ---------------
                    514: **     Calculates the offset from GMT in seconds
2.1       frystyk   515: */
2.3       frystyk   516: PUBLIC time_t HTGetTimeZoneOffset (void)
2.1       frystyk   517: {
2.3       frystyk   518:     time_t HTTimeZone = 0;
                    519: #ifdef HAVE_TIMEZONE
                    520:     {
                    521:        time_t cur_t = time(NULL);
                    522: #ifdef HT_REENTRANT
                    523:        struct tm loctime;
                    524:        struct tm *local = (struct tm *) localtime_r(&cur_t, &loctime);
                    525: #else
                    526:        struct tm *local = localtime(&cur_t);
                    527: #endif /* HT_REENTRANT */
                    528:        if (daylight && local->tm_isdst>0) {               /* daylight time? */
                    529: #ifdef HAVE_ALTZONE
                    530:            HTTimeZone = altzone;
                    531: #else
                    532:            /* Assumes a fixed DST offset of 1 hour, which is probably wrong */
                    533:            HTTimeZone = timezone - 3600;
                    534: #endif /* HAVE_ALTZONE */
                    535:        } else {                                                       /* no */
                    536:            HTTimeZone = timezone;
                    537:        }
                    538:        HTTimeZone = -HTTimeZone;
                    539:        if (CORE_TRACE)
                    540:            HTTrace("TimeZone.... GMT + (%02d) hours (including DST)\n",
                    541:                    (int) HTTimeZone/3600);
                    542:     }
                    543: #else
                    544: #ifdef HAVE_TM_GMTOFF
                    545:     {
                    546:        time_t cur_t = time(NULL);
                    547: #ifdef HT_REENTRANT
                    548:        struct tm loctime;
                    549:        localtime_r(&cur_t, &loctime);
                    550: #else
                    551:        struct tm * local = localtime(&cur_t);
                    552: #endif /* HT_REENTRANT */
                    553:        HTTimeZone = local->tm_gmtoff;
                    554:        if (CORE_TRACE)
                    555:            HTTrace("TimeZone.... GMT + (%02d) hours (including DST)\n",
                    556:                    (int)local->tm_gmtoff / 3600);
                    557:     }
                    558: #else
                    559:     if (CORE_TRACE) HTTrace("TimeZone.... Not defined\n");
                    560: #endif /* HAVE_TM_GMTOFF */
                    561: #endif /* HAVE_TIMEZONE */
                    562:     return HTTimeZone;
2.1       frystyk   563: }

Webmaster