home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / mkparse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  47.2 KB  |  1,892 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* URL/NET parsing routines
  19.  * Additions/Changes by Judson Valeski 1997
  20.  */
  21.                
  22. #include "mkutils.h"
  23. #include "xp.h"
  24. #include "mkparse.h"
  25. #include "mknews.h"
  26. #include "xp_time.h"
  27. #include "mkfsort.h"
  28.  
  29. extern int MK_OUT_OF_MEMORY;
  30.  
  31. #ifdef PROFILE
  32. #pragma profile on
  33. #endif
  34.  
  35. /* global */
  36. XP_List * ExternalURLTypeList=0;
  37.  
  38. #define EXTERNAL_TYPE_URL  999  /* a special external URL type */
  39.  
  40. #define HEX_ESCAPE '%'
  41.  
  42. /* add an external URL type prefix 
  43.  *
  44.  * this prefix will be used to see if a URL is an absolute
  45.  * or a relative URL.
  46.  * 
  47.  * you should just pass in the prefix of the URL
  48.  *
  49.  * for instance "http" or "ftp" or "foobar".
  50.  *
  51.  * do not pass in a colon or double slashes.
  52.  */
  53. PUBLIC void 
  54. NET_AddExternalURLType(char * type)
  55. {
  56.     XP_List * list_ptr = ExternalURLTypeList;
  57.     char * new_type=0;
  58.     char * reg_type;
  59.  
  60.     while((reg_type = (char *)XP_ListNextObject(list_ptr)) != NULL)
  61.       {
  62.         if(!strcasecomp(reg_type, type))
  63.             return;  /* ignore duplicates */
  64.       }
  65.  
  66.     StrAllocCopy(new_type, type);
  67.  
  68.     if(!ExternalURLTypeList)
  69.         ExternalURLTypeList = XP_ListNew();
  70.  
  71.     XP_ListAddObject(ExternalURLTypeList, new_type);
  72.     
  73. }
  74.  
  75. /* remove an external URL type prefix 
  76.  */
  77. PUBLIC void 
  78. NET_DelExternalURLType(char * type)
  79. {
  80.     XP_List * list_ptr = ExternalURLTypeList;
  81.     char * reg_type;
  82.  
  83.     while((reg_type = (char *)XP_ListNextObject(list_ptr)) != NULL)
  84.       {
  85.         if(!strcasecomp(reg_type, type))
  86.           {
  87.             /* found it */
  88.             XP_ListRemoveObject(ExternalURLTypeList, reg_type);
  89.             return; /* done */ 
  90.           }
  91.       }
  92. }
  93.  
  94. /* check the passed in url to see if it is an
  95.  * external type url
  96.  */
  97. PRIVATE int
  98. net_CheckForExternalURLType(char * url)
  99. {
  100.     XP_List * list_ptr = ExternalURLTypeList;
  101.     char * reg_type;
  102.     int len;
  103.  
  104.     while((reg_type = (char *)XP_ListNextObject(list_ptr)) != NULL)
  105.       {
  106.         len = XP_STRLEN(reg_type);
  107.         if(!strncasecomp(reg_type, url, len) && url[len] == ':')
  108.           { 
  109.             /* found it */
  110.             return EXTERNAL_TYPE_URL; /* done */ 
  111.           }
  112.       }
  113.  
  114.     return(0);
  115. }
  116.  
  117. /* modifies a url of the form   /foo/../foo1  ->  /foo1
  118.  *
  119.  * it only operates on "file" "ftp" and "http" url's all others are returned
  120.  * unmodified
  121.  *
  122.  * returns the modified passed in URL string
  123.  */
  124. PRIVATE char *
  125. net_ReduceURL (char *url)
  126. {
  127.     int url_type = NET_URL_Type(url);
  128.     char * fwd_ptr;
  129.     char * url_ptr;
  130.     char * path_ptr;
  131.  
  132.     if(!url)
  133.         return(url);
  134.  
  135.     if(url_type == HTTP_TYPE_URL || url_type == FILE_TYPE_URL ||
  136.        url_type == FTP_TYPE_URL ||
  137.        url_type == SECURE_HTTP_TYPE_URL ||
  138.        url_type == MARIMBA_TYPE_URL)
  139.       {
  140.  
  141.         /* find the path so we only change that and not the host
  142.          */
  143.         path_ptr = XP_STRCHR(url, '/');
  144.  
  145.         if(!path_ptr)
  146.             return(url);
  147.  
  148.         if(*(path_ptr+1) == '/')
  149.             path_ptr = XP_STRCHR(path_ptr+2, '/');
  150.             
  151.         if(!path_ptr)
  152.             return(url);
  153.  
  154.         fwd_ptr = path_ptr;
  155.         url_ptr = path_ptr;
  156.  
  157.         for(; *fwd_ptr != '\0'; fwd_ptr++)
  158.           {
  159.             
  160.             if(*fwd_ptr == '/' && *(fwd_ptr+1) == '.' && *(fwd_ptr+2) == '/')
  161.               {
  162.                 /* remove ./ 
  163.                  */    
  164.                 fwd_ptr += 1;
  165.               }
  166.             else if(*fwd_ptr == '/' && *(fwd_ptr+1) == '.' && *(fwd_ptr+2) == '.' && 
  167.                                     (*(fwd_ptr+3) == '/' || *(fwd_ptr+3) == '\0'))
  168.               {
  169.                 /* remove foo/.. 
  170.                  */    
  171.                 /* reverse the url_ptr to the previous slash
  172.                  */
  173.                 if(url_ptr != path_ptr) 
  174.                     url_ptr--; /* we must be going back at least one */
  175.                 for(;*url_ptr != '/' && url_ptr != path_ptr; url_ptr--)
  176.                     ;  /* null body */
  177.     
  178.                 /* forward the fwd_prt past the ../
  179.                  */
  180.                 fwd_ptr += 2;
  181.               }
  182.             else
  183.               {
  184.                 /* copy the url incrementaly 
  185.                  */
  186.                 *url_ptr++ = *fwd_ptr;
  187.               }
  188.           }
  189.         *url_ptr = '\0';  /* terminate the url */
  190.       }
  191.  
  192.     return(url);
  193. }
  194.         
  195. /* Makes a relative URL into an absolute one.  
  196.  *
  197.  * If an absolute url is passed in it will be copied and returned.
  198.  *
  199.  * Always returns a malloc'd string or NULL on out of memory error
  200.  */
  201. PUBLIC char *
  202. NET_MakeAbsoluteURL(char * absolute_url, char * relative_url)
  203. {
  204.     char * ret_url=0;
  205.     int    new_length;
  206.     char * cat_point=0;
  207.     char   cat_point_char;
  208.     int    url_type=0;
  209.     int    base_type;
  210.  
  211.     /* if either is NULL
  212.      */
  213.     if(!absolute_url || !relative_url)
  214.       {
  215.         StrAllocCopy(ret_url, relative_url);
  216.         return(ret_url);
  217.       }
  218.  
  219.     /* use the URL_Type function to figure
  220.      * out if it's a recognized URL method
  221.      */
  222.     url_type = NET_URL_Type(relative_url);
  223.  
  224.     /* there are some extra cases we need to catch
  225.      */
  226.     if(!url_type)
  227.       {
  228.        switch(*relative_url) 
  229.         {
  230.           case 'i':
  231.             if(!XP_STRNCMP(relative_url,"internal-icon-", 14)
  232.                    || !XP_STRNCMP(relative_url,"internal-external-reconnect:", 28)
  233.                      || !XP_STRCMP(relative_url,"internal-external-plugin"))
  234.             url_type = INTERNAL_IMAGE_TYPE_URL;
  235.             break;
  236.           case '/':
  237.             if(!strncasecomp(relative_url, "/mc-icons/", 10) ||
  238.                !strncasecomp(relative_url, "/ns-icons/", 10))
  239.                 {
  240.                 if(!XP_STRCMP(relative_url+10, "menu.gif"))
  241.                     url_type = INTERNAL_IMAGE_TYPE_URL;
  242.                 else if(!XP_STRCMP(relative_url+10, "unknown.gif"))
  243.                     url_type = INTERNAL_IMAGE_TYPE_URL;
  244.                 else if(!XP_STRCMP(relative_url+10, "text.gif"))
  245.                     url_type = INTERNAL_IMAGE_TYPE_URL;
  246.                 else if(!XP_STRCMP(relative_url+10, "image.gif"))
  247.                     url_type = INTERNAL_IMAGE_TYPE_URL;
  248.                 else if(!XP_STRCMP(relative_url+10, "sound.gif"))
  249.                     url_type = INTERNAL_IMAGE_TYPE_URL;
  250.                 else if(!XP_STRCMP(relative_url+10, "binary.gif"))
  251.                     url_type = INTERNAL_IMAGE_TYPE_URL;
  252.                 else if(!XP_STRCMP(relative_url+10, "movie.gif"))
  253.                     url_type = INTERNAL_IMAGE_TYPE_URL;
  254.                 }
  255.             break;
  256.         }
  257.       }
  258.     else if(url_type == ABOUT_TYPE_URL)
  259.       {
  260.         /* don't allow about:cache in a document */
  261.         if(!strncasecomp(relative_url, "about:cache", 11)
  262.            || !strncasecomp(relative_url, "about:global", 12)
  263.            || !strncasecomp(relative_url, "about:image-cache", 17)
  264.            || !strncasecomp(relative_url, "about:memory-cache", 18))
  265.           {
  266.             return XP_STRDUP("");
  267.           }
  268.       }
  269.  
  270.     if(!url_type)
  271.         url_type = net_CheckForExternalURLType(relative_url);
  272.  
  273.     if(url_type)
  274.       {
  275.         /* it's either an absolute url
  276.          * or a messed up one of the type proto:/path
  277.          * but notice the missing host.
  278.          */
  279.         char * colon = XP_STRCHR(relative_url, ':'); /* must be there */
  280.  
  281.         if( (colon && *(colon+1) == '/' && *(colon+2) == '/') || 
  282.             (url_type != GOPHER_TYPE_URL
  283.              && url_type != FTP_TYPE_URL
  284.               && url_type != HTTP_TYPE_URL
  285.              && url_type != SECURE_HTTP_TYPE_URL
  286.              && url_type != RLOGIN_TYPE_URL
  287.              && url_type != TELNET_TYPE_URL
  288.              && url_type != TN3270_TYPE_URL
  289.              && url_type != WAIS_TYPE_URL) )
  290.           {
  291.             /* it appears to have all it's parts.  Assume it's completely
  292.              * absolute
  293.              */
  294.             StrAllocCopy(ret_url, relative_url);
  295.             return(ret_url);
  296.           }
  297.         else
  298.           {
  299.             /* it's a screwed up relative url of the form http:[relative url]
  300.              * remove the stuff before and at the colon and treat it as a normal
  301.              * relative url
  302.              */
  303.             char * colon = XP_STRCHR(relative_url, ':');
  304.  
  305.             relative_url = colon+1;
  306.           }
  307.       }
  308.  
  309.  
  310.     /* At this point, we know that `relative_url' is not absolute.
  311.        If the base URL, `absolute_url', is a "mailbox:" URL, then
  312.        we should not allow relative expansion: that is,
  313.        NET_MakeAbsoluteURL("mailbox:/A/B/C", "YYY") should not
  314.        return "mailbox:/A/B/YYY".
  315.  
  316.        However, expansion of "#" and "?" parameters should work:
  317.        NET_MakeAbsoluteURL("mailbox:/A/B/C?id=XXX", "#part2")
  318.        should be allowed to expand to "mailbox:/A/B/C?id=XXX#part2".
  319.  
  320.        If you allow random HREFs in attached HTML to mail messages to
  321.        expand to mailbox URLs, then bad things happen -- among other
  322.        things, an entry will be automatically created in the folders
  323.        list for each of these bogus non-files.
  324.  
  325.        It's an open question as to whether relative expansion should
  326.        be allowed for news: and snews: URLs.
  327.  
  328.        Reasons to allow it:
  329.  
  330.          =  ClariNet has been using it
  331.  
  332.          =  (Their reason:) it's the only way for an HTML news message
  333.             to refer to another HTML news message in a way which
  334.             doesn't make assumptions about the name of the news host,
  335.             and which also doesn't assume that the message is on the
  336.             default news host.
  337.  
  338.        Reasons to disallow it:
  339.  
  340.          =  Consistency with "mailbox:"
  341.  
  342.          =  If there is a news message which has type text/html, and
  343.             which has a relative URL like <IMG SRC="foo.gif"> in it,
  344.             but which has no <BASE> tag, then we would expand that
  345.             image to "news:foo.gif".  Which would fail, of course,
  346.             but which might annoy news admins by generating bogus
  347.             references.
  348.  
  349.        So for now, let's allow 
  350.        NET_MakeAbsoluteURL("news:123@4", "456@7") => "news:456@7"; and
  351.        NET_MakeAbsoluteURL("news://h/123@4", "456@7") => "news://h/456@7".
  352.      */
  353.     base_type = NET_URL_Type(absolute_url);
  354.     if ((base_type == MAILBOX_TYPE_URL || base_type == IMAP_TYPE_URL || base_type == NEWS_TYPE_URL) &&
  355.         *relative_url != '#' &&
  356.         *relative_url != '?')
  357.       {
  358.         return XP_STRDUP("");
  359.       }
  360.  
  361.     if(relative_url[0] == '/' && relative_url[1] == '/')
  362.       {
  363.         /* a host absolute URL
  364.          */
  365.  
  366.         /* find the colon after the protocol
  367.          */
  368.         cat_point = XP_STRCHR(absolute_url, ':');
  369.         if (cat_point && base_type == WYSIWYG_TYPE_URL)
  370.             cat_point = XP_STRCHR(cat_point + 1, ':');
  371.  
  372.         /* append after the colon
  373.          */
  374.         if(cat_point)
  375.            cat_point++;
  376.  
  377.       }
  378.     else if(relative_url[0] == '/')
  379.       {
  380.         /* a path absolute URL
  381.          * append at the slash after the host part
  382.          */
  383.  
  384.         /* find the colon after the protocol 
  385.          */
  386.         char *colon = XP_STRCHR(absolute_url, ':');
  387.         if (colon && base_type == WYSIWYG_TYPE_URL)
  388.             colon = XP_STRCHR(colon + 1, ':');
  389.  
  390.         if(colon)
  391.           {
  392.             if(colon[1] == '/' && colon[2] == '/')
  393.               {
  394.                 /* find the next slash 
  395.                  */
  396.                 cat_point = XP_STRCHR(colon+3, '/');
  397.  
  398.                 if(!cat_point)
  399.                   {
  400.                     /* if there isn't another slash then the cat point is the very end
  401.                      */
  402.                     cat_point = &absolute_url[XP_STRLEN(absolute_url)];
  403.                   }
  404.               }
  405.             else
  406.               {
  407.                 /* no host was given so the cat_point is right after the colon
  408.                  */
  409.                 cat_point = colon+1;
  410.               }
  411.  
  412. #if defined(XP_WIN) || defined(XP_OS2)
  413.             /* this will allow drive letters to work right on windows 
  414.              */
  415.             if(XP_IS_ALPHA(*cat_point) && *(cat_point+1) == ':')
  416.                 cat_point += 2;
  417. #endif /* XP_WIN */
  418.  
  419.           }
  420.       }
  421.     else if(relative_url[0] == '#')
  422.       {
  423.         /* a positioning within the same document relative url
  424.          *
  425.          * add teh relative url to the full text of the absolute url minus
  426.          * any # punctuation the url might have
  427.           *
  428.          */
  429.         char * hash = XP_STRCHR(absolute_url, '#');
  430.     
  431.         if(hash)
  432.           {
  433.             char * ques_mark = XP_STRCHR(absolute_url, '?');
  434.    
  435.             if(ques_mark)
  436.               {
  437.                 /* this is a hack.
  438.                  * copy things to try and make them more correct
  439.                  */
  440.                 *hash = '\0';
  441.  
  442.                 StrAllocCopy(ret_url, absolute_url);
  443.                 StrAllocCat(ret_url, relative_url);
  444.                 StrAllocCat(ret_url, ques_mark);
  445.  
  446.                 *hash = '#';
  447.                 
  448.                 return(net_ReduceURL(ret_url));
  449.               }
  450.  
  451.             cat_point = hash;
  452.           }
  453.         else
  454.           {
  455.             cat_point = &absolute_url[XP_STRLEN(absolute_url)]; /* the end of the URL */
  456.           }
  457.       }
  458.     else
  459.       {
  460.         /* a completely relative URL
  461.          *
  462.          * append after the last slash
  463.          */
  464.         char * ques = XP_STRCHR(absolute_url, '?');
  465.         char * hash = XP_STRCHR(absolute_url, '#');
  466.  
  467.         if(ques)
  468.             *ques = '\0';
  469.  
  470.         if(hash)
  471.             *hash = '\0';
  472.  
  473.         cat_point = XP_STRRCHR(absolute_url, '/');
  474.  
  475.         /* if there are no slashes then append right after the colon after the protocol
  476.          */
  477.         if(!cat_point)
  478.             cat_point = XP_STRCHR(absolute_url, ':');
  479.  
  480.         /* set the value back 
  481.          */
  482.         if(ques)
  483.             *ques = '?';
  484.  
  485.         if(hash)
  486.             *hash = '#';
  487.  
  488.         if(cat_point)
  489.             cat_point++;  /* append right after the slash or colon not on it */
  490.       }
  491.     
  492.     if(cat_point)
  493.       {
  494.         cat_point_char = *cat_point;  /* save the value */
  495.         *cat_point = '\0';
  496.         new_length = XP_STRLEN(absolute_url) + XP_STRLEN(relative_url) + 1;
  497.         ret_url = (char *) XP_ALLOC(new_length);
  498.         if(!ret_url)
  499.             return(NULL);  /* out of memory */
  500.  
  501.         XP_STRCPY(ret_url, absolute_url);
  502.         XP_STRCAT(ret_url, relative_url);
  503.         *cat_point = cat_point_char;  /* set the absolute url back to its original state */
  504.       } 
  505.     else
  506.       {
  507.         /* something went wrong.  just return a copy of the relative url
  508.          */
  509.         StrAllocCopy(ret_url, relative_url);
  510.       }
  511.  
  512.     return(net_ReduceURL(ret_url));
  513. }
  514.         
  515. /*
  516.  *  Returns the number of the month given or -1 on error.
  517.  */
  518. MODULE_PRIVATE int NET_MonthNo (char * month)
  519. {
  520.     int ret;
  521.     if (!strncasecomp(month, "JAN", 3))
  522.         ret = 0;
  523.     else if (!strncasecomp(month, "FEB", 3))
  524.         ret = 1;
  525.     else if (!strncasecomp(month, "MAR", 3))
  526.         ret = 2;
  527.     else if (!strncasecomp(month, "APR", 3))
  528.         ret = 3;
  529.     else if (!strncasecomp(month, "MAY", 3))
  530.         ret = 4;
  531.     else if (!strncasecomp(month, "JUN", 3))
  532.         ret = 5;
  533.     else if (!strncasecomp(month, "JUL", 3))
  534.         ret = 6;
  535.     else if (!strncasecomp(month, "AUG", 3))
  536.         ret = 7;
  537.     else if (!strncasecomp(month, "SEP", 3))
  538.         ret = 8;
  539.     else if (!strncasecomp(month, "OCT", 3))
  540.         ret = 9;
  541.     else if (!strncasecomp(month, "NOV", 3))
  542.         ret = 10;
  543.     else if (!strncasecomp(month, "DEC", 3))
  544.         ret = 11;
  545.     else 
  546.       {
  547.         ret = -1;
  548.         TRACEMSG(("net_convert_month. Couldn't resolve date.\n"));
  549.       }
  550.     return ret;
  551. }
  552.  
  553.  
  554. /* accepts an RFC 850 or RFC 822 date string and returns a
  555.  * time_t
  556.  */
  557. MODULE_PRIVATE time_t 
  558. NET_ParseDate(char *date_string)
  559. {
  560. #ifndef USE_OLD_TIME_FUNC
  561.     time_t date;
  562.  
  563.     TRACEMSG(("Parsing date string: %s\n",date_string));
  564.  
  565.     /* try using XP_ParseTimeString instead
  566.      */
  567.     date = XP_ParseTimeString(date_string, TRUE);
  568.  
  569.     if(date)
  570.       {
  571.         TRACEMSG(("Parsed date as GMT: %s\n", asctime(gmtime(&date))));
  572.         TRACEMSG(("Parsed date as local: %s\n", ctime(&date)));
  573.       }
  574.     else
  575.       {
  576.         TRACEMSG(("Could not parse date"));
  577.       }
  578.  
  579.     return(date);
  580.  
  581. #else
  582.     struct tm time_info;         /* Points to static tm structure */
  583.     char  *ip;
  584.     char   mname[256];
  585.     time_t rv;
  586.  
  587.     TRACEMSG(("Parsing date string: %s\n",date_string));
  588.  
  589.     memset(&time_info, 0, sizeof(struct tm));
  590.  
  591.     /* Whatever format we're looking at, it will start with weekday. */
  592.     /* Skip to first space. */
  593.     if(!(ip = XP_STRCHR(date_string,' ')))
  594.         return 0;
  595.     else
  596.         while(XP_IS_SPACE(*ip))
  597.             ++ip;
  598.  
  599.     /* make sure that the date is less than 256 
  600.      * That will keep mname from ever overflowing 
  601.      */
  602.     if(255 < XP_STRLEN(ip))
  603.         return(0);
  604.  
  605.     if(isalpha(*ip)) 
  606.       {
  607.         /* ctime */
  608.         sscanf(ip, (strstr(ip, "DST") ? "%s %d %d:%d:%d %*s %d"
  609.                     : "%s %d %d:%d:%d %d"),
  610.                mname,
  611.                &time_info.tm_mday,
  612.                &time_info.tm_hour,
  613.                &time_info.tm_min,
  614.                &time_info.tm_sec,
  615.                &time_info.tm_year);
  616.         time_info.tm_year -= 1900;
  617.       }
  618.     else if(ip[2] == '-') 
  619.       {
  620.         /* RFC 850 (normal HTTP) */
  621.         char t[256];
  622.         sscanf(ip,"%s %d:%d:%d", t,
  623.                                 &time_info.tm_hour,
  624.                                 &time_info.tm_min,
  625.                                 &time_info.tm_sec);
  626.         t[2] = '\0';
  627.         time_info.tm_mday = atoi(t);
  628.         t[6] = '\0';
  629.         XP_STRCPY(mname,&t[3]);
  630.         time_info.tm_year = atoi(&t[7]);
  631.         /* Prevent wraparound from ambiguity */
  632.         if(time_info.tm_year < 70)
  633.             time_info.tm_year += 100;
  634.         else if(time_info.tm_year > 1900)
  635.             time_info.tm_year -= 1900;
  636.       }
  637.     else 
  638.       {
  639.         /* RFC 822 */
  640.         sscanf(ip,"%d %s %d %d:%d:%d",&time_info.tm_mday,
  641.                                     mname,
  642.                                     &time_info.tm_year,
  643.                                     &time_info.tm_hour,
  644.                                     &time_info.tm_min,
  645.                                     &time_info.tm_sec);
  646.         /* since tm_year is years since 1900 and the year we parsed
  647.           * is absolute, we need to subtract 1900 years from it
  648.          */
  649.         time_info.tm_year -= 1900;
  650.       }
  651.     time_info.tm_mon = NET_MonthNo(mname);
  652.  
  653.     if(time_info.tm_mon == -1) /* check for error */
  654.         return(0);
  655.  
  656.  
  657.     TRACEMSG(("Parsed date as: %s\n", asctime(&time_info)));
  658.  
  659.     rv = mktime(&time_info);
  660.     
  661.     if(time_info.tm_isdst)
  662.         rv -= 3600;
  663.  
  664.     if(rv == -1)
  665.       {
  666.         TRACEMSG(("mktime was unable to resolve date/time: %s\n", asctime(&time_info)));
  667.         return(0);
  668.       }
  669.     else
  670.       {
  671.         TRACEMSG(("Parsed date %s\n", asctime(&time_info)));
  672.         TRACEMSG(("\tas %s\n", ctime(&rv)));
  673.         return(rv);
  674.       }
  675. #endif
  676. }
  677.  
  678. MODULE_PRIVATE char * 
  679. NET_ParseURL (const char *url, int parts_requested)
  680. {
  681.     char *rv=0,*colon, *slash, *ques_mark, *hash_mark, *atSign, *host, *passwordColon;
  682.  
  683.     assert(url);
  684.  
  685.     if(!url)
  686.         return(StrAllocCat(rv, ""));
  687.     colon = XP_STRCHR(url, ':'); /* returns a const char */
  688.  
  689.     /* Get the protocol part, not including anything beyond the colon */
  690.     if (parts_requested & GET_PROTOCOL_PART) {
  691.         if(colon) {
  692.             char val = *(colon+1);
  693.             *(colon+1) = '\0';
  694.             StrAllocCopy(rv, url);
  695.             *(colon+1) = val;
  696.  
  697.             /* If the user wants more url info, tack on extra slashes. */
  698.             if( (parts_requested & GET_HOST_PART)
  699.                 || (parts_requested & GET_USERNAME_PART)
  700.                 || (parts_requested & GET_PASSWORD_PART)
  701.                 )
  702.               {
  703.                 if( *(colon+1) == '/' && *(colon+2) == '/')
  704.                    StrAllocCat(rv, "//");
  705.                 /* If there's a third slash consider it file:/// and tack on the last slash. */
  706.                 if( *(colon+3) == '/' )
  707.                     StrAllocCat(rv, "/");
  708.               }
  709.           }
  710.       }
  711.  
  712.  
  713.     /* Get the username if one exists */
  714.     if (parts_requested & GET_USERNAME_PART) {
  715.         if (colon 
  716.             && (*(colon+1) == '/')
  717.             && (*(colon+2) == '/')
  718.             && (*(colon+3) != '\0')) {
  719.  
  720.             if ( (slash = XP_STRCHR(colon+3, '/')) != NULL)
  721.                 *slash = '\0';
  722.             if ( (atSign = XP_STRCHR(colon+3, '@')) != NULL) {
  723.                 *atSign = '\0';
  724.                 if ( (passwordColon = XP_STRCHR(colon+3, ':')) != NULL)
  725.                     *passwordColon = '\0';
  726.                 StrAllocCat(rv, colon+3);
  727.  
  728.                 /* Get the password if one exists */
  729.                 if (parts_requested & GET_PASSWORD_PART) {
  730.                     if (passwordColon) {
  731.                         StrAllocCat(rv, ":");
  732.                         StrAllocCat(rv, passwordColon+1);
  733.                     }
  734.                 }
  735.                 if (parts_requested & GET_HOST_PART)
  736.                     StrAllocCat(rv, "@");
  737.                 if (passwordColon)
  738.                     *passwordColon = ':';
  739.                 *atSign = '@';
  740.             }
  741.             if (slash)
  742.                 *slash = '/';
  743.         }
  744.     }
  745.  
  746.     /* Get the host part */
  747.     if (parts_requested & GET_HOST_PART) {
  748.         if(colon) {
  749.             if(*(colon+1) == '/' && *(colon+2) == '/')
  750.               {
  751.                 slash = XP_STRCHR(colon+3, '/');
  752.  
  753.                 if(slash)
  754.                     *slash = '\0';
  755.  
  756.                 if( (atSign = XP_STRCHR(colon+3, '@')) != NULL)
  757.                     host = atSign+1;
  758.                 else
  759.                     host = colon+3;
  760.                 
  761.                 ques_mark = XP_STRCHR(host, '?');
  762.  
  763.                 if(ques_mark)
  764.                     *ques_mark = '\0';
  765.  
  766. /*
  767.  * Protect systems whose header files forgot to let the client know when
  768.  * gethostbyname would trash their stack.
  769.  */
  770. #ifndef MAXHOSTNAMELEN
  771. #ifdef XP_OS2
  772. #error Managed to get into NET_ParseURL without defining MAXHOSTNAMELEN !!!
  773. #endif
  774. #endif
  775.                 /* limit hostnames to within MAXHOSTNAMELEN characters to keep
  776.                  * from crashing
  777.                  */
  778.                 if(XP_STRLEN(host) > MAXHOSTNAMELEN)
  779.                   {
  780.                     char * cp;
  781.                     char old_char;
  782.  
  783.                     cp = host+MAXHOSTNAMELEN;
  784.                     old_char = *cp;
  785.  
  786.                     *cp = '\0';
  787.  
  788.                     StrAllocCat(rv, host);
  789.  
  790.                     *cp = old_char;
  791.                   }
  792.                 else
  793.                   {
  794.                     StrAllocCat(rv, host);
  795.                   }
  796.  
  797.                 if(slash)
  798.                     *slash = '/';
  799.  
  800.                 if(ques_mark)
  801.                     *ques_mark = '?';
  802.               }
  803.           }
  804.       }
  805.     
  806.     /* Get the path part */
  807.     if (parts_requested & GET_PATH_PART) {
  808.         if(colon) {
  809.             if(*(colon+1) == '/' && *(colon+2) == '/')
  810.               {
  811.                 /* skip host part */
  812.                 slash = XP_STRCHR(colon+3, '/');
  813.               }
  814.             else 
  815.               {
  816.                 /* path is right after the colon
  817.                  */
  818.                 slash = colon+1;
  819.               }
  820.                 
  821.             if(slash)
  822.               {
  823.                 ques_mark = XP_STRCHR(slash, '?');
  824.                 hash_mark = XP_STRCHR(slash, '#');
  825.  
  826.                 if(ques_mark)
  827.                     *ques_mark = '\0';
  828.     
  829.                 if(hash_mark)
  830.                     *hash_mark = '\0';
  831.  
  832.                 StrAllocCat(rv, slash);
  833.  
  834.                 if(ques_mark)
  835.                     *ques_mark = '?';
  836.     
  837.                 if(hash_mark)
  838.                     *hash_mark = '#';
  839.               }
  840.           }
  841.       }
  842.         
  843.     if(parts_requested & GET_HASH_PART) {
  844.         hash_mark = XP_STRCHR(url, '#'); /* returns a const char * */
  845.  
  846.         if(hash_mark)
  847.           {
  848.             ques_mark = XP_STRCHR(hash_mark, '?');
  849.  
  850.             if(ques_mark)
  851.                 *ques_mark = '\0';
  852.  
  853.             StrAllocCat(rv, hash_mark);
  854.  
  855.             if(ques_mark)
  856.                 *ques_mark = '?';
  857.           }
  858.       }
  859.  
  860.     if(parts_requested & GET_SEARCH_PART) {
  861.         ques_mark = XP_STRCHR(url, '?'); /* returns a const char * */
  862.  
  863.         if(ques_mark)
  864.           {
  865.             hash_mark = XP_STRCHR(ques_mark, '#');
  866.  
  867.             if(hash_mark)
  868.                 *hash_mark = '\0';
  869.  
  870.             StrAllocCat(rv, ques_mark);
  871.  
  872.             if(hash_mark)
  873.                 *hash_mark = '#';
  874.           }
  875.       }
  876.  
  877.     /* copy in a null string if nothing was copied in
  878.      */
  879.     if(!rv)
  880.        StrAllocCopy(rv, "");
  881.     
  882.     /* XP_OS2_FIX IBM-MAS: long URLS blowout tracemsg buffer, 
  883.         set max to 1900 chars */
  884.     TRACEMSG(("mkparse: ParseURL: parsed - %-.1900s",rv));
  885.  
  886.     return rv;
  887. }
  888.  
  889. /* DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER
  890.    DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER
  891.    DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER
  892.  
  893.                                    TOXIC!
  894.  
  895.      Do not even think of changing IS_OK or anything anywhere near it.
  896.  
  897.      There is much spooky voodoo involving signed and unsigned chars
  898.      when using gcc on SunOS 4.1.3.
  899.  
  900.      If you think you understand it, the smart money says you're wrong.
  901.  
  902.                                    TOXIC!
  903.  
  904.    DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER
  905.    DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER
  906.    DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER
  907.  */
  908.  
  909. /* encode illegal characters into % escaped hex codes.
  910.  *
  911.  * mallocs and returns a string that must be freed
  912.  */
  913. PRIVATE CONST 
  914. int netCharType[256] =
  915. /*    Bit 0        xalpha        -- the alphas
  916. **    Bit 1        xpalpha        -- as xalpha but 
  917. **                             converts spaces to plus and plus to %20
  918. **    Bit 3 ...    path        -- as xalphas but doesn't escape '/'
  919. */
  920.     /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
  921.     {    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 0x */
  922.          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 1x */
  923.          0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4,    /* 2x   !"#$%&'()*+,-./     */
  924.          7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,    /* 3x  0123456789:;<=>?     */
  925.          7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,    /* 4x  @ABCDEFGHIJKLMNO  */
  926.          7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7,    /* 5X  PQRSTUVWXYZ[\]^_     */
  927.          0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,    /* 6x  `abcdefghijklmno     */
  928.          7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,    /* 7X  pqrstuvwxyz{\}~    DEL */
  929.          0, };
  930.  
  931. #define IS_OK(C) (netCharType[((unsigned int) (C))] & (mask))
  932.  
  933. PUBLIC char *
  934. NET_Escape (const char * str, int mask)
  935. {
  936.     if(!str)
  937.         return NULL;
  938.     return NET_EscapeBytes (str, (int32)XP_STRLEN(str), mask, NULL);
  939. }
  940.  
  941. PUBLIC char *
  942. NET_EscapeBytes (const char * str, int32 len, int mask, int32 * out_len)
  943. {
  944.     register CONST unsigned char *src;
  945.     register unsigned char *dst;
  946.     char        *result;
  947.     int32       i, extra = 0;
  948.     char        *hexChars = "0123456789ABCDEF";
  949.  
  950.     if(!str)
  951.         return(0);
  952.  
  953.     src = (unsigned char *) str;
  954.     for(i = 0; i < len; i++)
  955.       {
  956.         if (!IS_OK(src[i]))
  957.             extra+=2; /* the escape, plus an extra byte for each nibble */
  958.       }
  959.  
  960.     if(!(result = (char *) XP_ALLOC(len + extra + 1)))
  961.         return(0);
  962.  
  963.     dst = (unsigned char *) result;
  964.     for(i = 0; i < len; i++)
  965.       {
  966.         unsigned char c = src[i];
  967.         if (IS_OK(c))
  968.           {
  969.             *dst++ = c;
  970.           }
  971.         else if(mask == URL_XPALPHAS && c == ' ')
  972.           {
  973.             *dst++ = '+'; /* convert spaces to pluses */
  974.           }
  975.         else 
  976.           {
  977.             *dst++ = HEX_ESCAPE;
  978.             *dst++ = hexChars[c >> 4];        /* high nibble */
  979.             *dst++ = hexChars[c & 0x0f];    /* low nibble */
  980.           }
  981.       }
  982.  
  983.     *dst = '\0';     /* tack on eos */
  984.     if(out_len)
  985.         *out_len = dst - (unsigned char *) result;
  986.     return result;
  987. }
  988.  
  989. /* return the size of a string if it were to be escaped.
  990.  */
  991. MODULE_PRIVATE int32
  992. NET_EscapedSize (const char * str, int mask)
  993. {
  994.     int32                         extra = 0;
  995.     register CONST unsigned char * src;
  996.  
  997.     if(!str)
  998.         return(0);
  999.  
  1000.     for(src=((unsigned char *)str); *src; src++)
  1001.         if (!IS_OK(*src))
  1002.             extra+=2; /* the escape, plus an extra byte for each nibble */
  1003.  
  1004.     return((int32)(src - ((unsigned char *)str)) + extra + 1);
  1005. }
  1006.  
  1007.  
  1008. /* decode % escaped hex codes into character values
  1009.  */
  1010. #define UNHEX(C) \
  1011.     ((C >= '0' && C <= '9') ? C - '0' : \
  1012.      ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
  1013.      ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
  1014.  
  1015. /* In-place rewrite of str, code copied from NET_UnEscapeCnt to avoid imposing
  1016.  * an XP_STRLEN and making that function call this one.
  1017.  */
  1018. PUBLIC int32
  1019. NET_UnEscapeBytes (char * str, int32 len)
  1020. {
  1021.     int32 i = 0;
  1022.     register char *dst = str;
  1023.     char c, d;
  1024.  
  1025.     while (i < len)
  1026.       {
  1027.         c = str[i++];
  1028.         if (c == HEX_ESCAPE && i < len)
  1029.           {
  1030.             d = str[i++];
  1031.             c = UNHEX(d);
  1032.             if (i < len)
  1033.               {
  1034.                 d = str[i++];
  1035.                 c = (c << 4) | UNHEX(d);
  1036.               }
  1037.           }
  1038.         *dst++ = c;
  1039.       }
  1040.     *dst = '\0';
  1041.  
  1042.     return (int32)(dst - str);
  1043. }
  1044.  
  1045. PUBLIC int
  1046. NET_UnEscapeCnt (char * str)
  1047. {
  1048.     register char *src = str;
  1049.     register char *dst = str;
  1050.  
  1051.     while(*src)
  1052.         if (*src != HEX_ESCAPE)
  1053.           {
  1054.             *dst++ = *src++;
  1055.           }
  1056.         else     
  1057.           {
  1058.             src++; /* walk over escape */
  1059.             if (*src)
  1060.               {
  1061.                 *dst = UNHEX(*src) << 4;
  1062.                 src++;
  1063.               }
  1064.             if (*src)
  1065.               {
  1066.                 *dst = (*dst + UNHEX(*src));
  1067.                 src++;
  1068.               }
  1069.             dst++;
  1070.           }
  1071.  
  1072.     *dst = 0;
  1073.  
  1074.     return (int)(dst - str);
  1075.  
  1076. } /* NET_UnEscapeCnt */
  1077.  
  1078. /* Returns TRUE if URL type is HTTP_TYPE_URL or SECURE_HTTP_TYPE_URL */
  1079. PUBLIC XP_Bool
  1080. NET_IsHTTP_URL(const char *URL)
  1081. {
  1082.     int type = NET_URL_Type (URL);
  1083.     return ( type == HTTP_TYPE_URL || type == SECURE_HTTP_TYPE_URL );
  1084. }
  1085.  
  1086. /* Note: Should I move XP_ConvertUrlToLocalFile and XP_BackupFileName,
  1087.  *       (currently in winfe\fegui.cpp) to new file of EDT/XP stuff???
  1088. */
  1089.  
  1090. /* Converts absolute URL into an URL relative to the base URL
  1091.  * BasePath must be an HTTP: or FILE: URL type
  1092.  * input_url should be absolute url of the same type,
  1093.  *  but we pass it through NET_MakeAbsoluteURL so we can use
  1094.  *  relative input - ASSUMES SAME TYPE!
  1095.  * Return values defined in \include\net.h
  1096.  * Caller must XP_FREE the string
  1097. */
  1098. int NET_MakeRelativeURL( char *base_url,
  1099.                          char *input_url,
  1100.                          char **relative_url )
  1101. {
  1102.     int Result = NET_URL_FAIL;
  1103.     char *base_host = NULL;
  1104.     char *url_host = NULL;
  1105.     char *base_path = NULL;
  1106.     char *url_path = NULL;
  1107.     char *base_ptr = NULL;
  1108.     char *url_ptr = NULL;
  1109.     int  base_type, url_type;
  1110.     char *relative = NULL;    
  1111.     char *absolute_url;
  1112.     int  new_length;
  1113.     int  subdir_count = 0;
  1114.     int  i;
  1115.     char *colon;
  1116.     char *slash;
  1117.  
  1118.     if( base_url == NULL || input_url == NULL ||
  1119.         XP_STRLEN(base_url) == 0 || 
  1120.         XP_STRLEN(input_url) == 0  ){
  1121.         return NET_URL_FAIL;
  1122.     }
  1123.     
  1124.     /* Convert to absolute URL */
  1125.     absolute_url = NET_MakeAbsoluteURL(base_url, input_url);
  1126.     
  1127.     if(absolute_url == NULL){
  1128.         return NET_URL_FAIL;
  1129.     }    
  1130.  
  1131.     /* We can only hande HTTP and FILE types,
  1132.     */
  1133.     /* Extended to handle FTP urls, for remote editing of ftp documents */
  1134.     base_type = NET_URL_Type (base_url);
  1135.     url_type = NET_URL_Type (absolute_url);
  1136.     if ( url_type == 0 ||
  1137.          ! ( base_type == FILE_TYPE_URL ||
  1138.              base_type == HTTP_TYPE_URL ||
  1139.              base_type == SECURE_HTTP_TYPE_URL ||
  1140.              base_type == FTP_TYPE_URL) ||
  1141.          ! ( url_type == FILE_TYPE_URL ||
  1142.              url_type == HTTP_TYPE_URL ||
  1143.              url_type == SECURE_HTTP_TYPE_URL ||
  1144.              url_type == FTP_TYPE_URL ) ) {
  1145.         goto CLEAN_UP;
  1146.     }
  1147.  
  1148.  
  1149.     /* If not both of same type,
  1150.      *  we return as if different device
  1151.     */
  1152.     if ( url_type != base_type ) {
  1153.         Result = NET_URL_NOT_ON_SAME_DEVICE;
  1154.         goto CLEAN_UP;
  1155.     }
  1156.  
  1157.  
  1158. #ifdef XP_WIN
  1159.     /* Check for UNC filenames.  E.g. file:////wongfoo/vol1/test.html  
  1160.      * (\\wongfoo\vol1\test.html).
  1161.      * This is the cheap and dirty hack so things don't completely break.
  1162.      * We just leave the url absolute if either base or absolute URL is a
  1163.      * UNC filename. 
  1164.      *
  1165.      * We should really do the following:
  1166.      *    1. If both URLs are UNC file URLs *AND* the machine name is the same, 
  1167.      *       make the URL relative, else NET_URL_NOT_ON_SAME_DEVICE.
  1168.      *
  1169.      *    2. If only one is a UNC filename, NET_URL_NOT_ON_SAME_DEVICE
  1170.      */
  1171.     if (url_type == FILE_TYPE_URL) {  /* already checked that types are the same. */
  1172.       /* Looking for "file:////" */
  1173.       if (XP_STRLEN(base_url) >= 9 &&
  1174.           !XP_STRNCMP(base_url + 5,"////",4)) {
  1175.         Result = NET_URL_NOT_ON_SAME_DEVICE;
  1176.         goto CLEAN_UP;
  1177.       }  
  1178.       if (XP_STRLEN(absolute_url) >= 9 &&
  1179.           !XP_STRNCMP(absolute_url + 5,"////",4)) {
  1180.         Result = NET_URL_NOT_ON_SAME_DEVICE;
  1181.         goto CLEAN_UP;
  1182.       }  
  1183.     }
  1184. #endif
  1185.  
  1186.  
  1187.     /*  Different hosts are also = different devices */
  1188.     base_host = NET_ParseURL(base_url, GET_HOST_PART);
  1189.     url_host = NET_ParseURL(absolute_url, GET_HOST_PART);
  1190.     if (XP_STRCMP(base_host,url_host)){
  1191.         Result = NET_URL_NOT_ON_SAME_DEVICE;
  1192.         goto CLEAN_UP;
  1193.     }
  1194.  
  1195.     /* Similar to NET_ParseURL(GET_PATH_PART),
  1196.      *  but we don't strip named anchors and search strings
  1197.      */
  1198.     colon = (char*) XP_STRCHR(base_url, ':'); /* returns a const char * */
  1199.  
  1200.     if(colon) {
  1201.         if(*(colon+1) == '/' && *(colon+2) == '/') {
  1202.             /* skip host part */
  1203.             slash = XP_STRCHR(colon+3, '/');
  1204.         } else {
  1205.             /* path is right after the colon */
  1206.             slash = colon+1;
  1207.         }
  1208.         if ( slash ) {
  1209.             base_path = XP_STRDUP(slash);
  1210.         }
  1211.     }
  1212.     if ( base_path == NULL ){
  1213.         goto CLEAN_UP;
  1214.     }
  1215.  
  1216.     colon = (char*) XP_STRCHR(absolute_url, ':');
  1217.  
  1218.     /* For url_path, find beginning of path in our 
  1219.      * already-allocated "absolute_url" string
  1220.      * Don't free url_path!
  1221.     */
  1222.     if(colon) {
  1223.         if(*(colon+1) == '/' && *(colon+2) == '/') {
  1224.             url_path = XP_STRCHR(colon+3, '/');
  1225.         } else {
  1226.             url_path = colon+1;
  1227.         }
  1228.     }
  1229.     if ( url_path == NULL ){
  1230.         goto CLEAN_UP;
  1231.     }
  1232.  
  1233.     /* TODO: MODIFY FOR NETWORK FILENAMES: //netname//blah */
  1234.  
  1235.     /* Find location of drive separator */
  1236.     base_ptr = (char*) XP_STRCHR(base_path, '|');
  1237.     url_ptr = (char*) XP_STRCHR(url_path, '|');
  1238.  
  1239.     /* Test if drive is different */
  1240.     if ( base_ptr && url_ptr &&
  1241. #ifdef XP_UNIX
  1242.         /* Case-sensitive for UNIX */
  1243.         *(url_ptr-1) != *(base_ptr-1) )
  1244. #else
  1245.         XP_TO_LOWER(*(url_ptr-1)) != XP_TO_LOWER(*(base_ptr-1)) )
  1246. #endif
  1247.     {
  1248.         Result = NET_URL_NOT_ON_SAME_DEVICE;
  1249.         goto CLEAN_UP;
  1250.     }
  1251.     
  1252.     if (base_ptr){
  1253.         base_ptr++; /* Move to first "/" after drive separator */
  1254.     } else {
  1255.         /* No '|', so start at begining of path */
  1256.         base_ptr = base_path;
  1257.     }
  1258.  
  1259.     if(url_ptr){
  1260.         url_ptr++;
  1261.     }else{
  1262.         /* We'll just work with copied string here */
  1263.         url_ptr = url_path;
  1264.     }
  1265.  
  1266.     /*Find first mismatched character */
  1267.     while ( 
  1268. #ifdef XP_UNIX
  1269.             /* Case-sensitive for UNIX */
  1270.             *base_ptr == *url_ptr && 
  1271. #else
  1272.             XP_TO_LOWER(*base_ptr) == XP_TO_LOWER(*url_ptr) &&
  1273. #endif
  1274.             *base_ptr != '\0' && *url_ptr != '\0' ){
  1275.         base_ptr++;
  1276.         url_ptr++;
  1277.     }
  1278.     
  1279.     /* If we next char is "#" and no base is empty, 
  1280.      *   then we have a target in the same document 
  1281.      *   We are done.
  1282.      */
  1283.     if( *url_ptr == '#' && *base_ptr == '\0' ){
  1284.         Result = NET_URL_SAME_DIRECTORY;
  1285.     } else {
  1286.         /* Backup to just after previous slash */
  1287.         while ( *(url_ptr-1) != '/' && url_ptr != url_path){ url_ptr--; }
  1288.  
  1289.         /* If we have more left on the base:
  1290.          *   either the base filename or more subdirectories,
  1291.          *   so count slashes to get number of subdirectories
  1292.          */
  1293.         while ( *base_ptr != '\0' ){
  1294.             if (*base_ptr == '/' ){
  1295.                 subdir_count++;
  1296.             }
  1297.             base_ptr++;
  1298.         }
  1299.  
  1300.         /* We are in the same directory if there are no slashes
  1301.          *  in the final relative path
  1302.          */
  1303.         if ( subdir_count == 0 && NULL == XP_STRCHR(url_ptr, '/') ){
  1304.             Result = NET_URL_SAME_DIRECTORY;
  1305.         } else {
  1306.             Result = NET_URL_SAME_DEVICE;
  1307.         }
  1308.     }
  1309.  
  1310.     /* Don't supply relative URL string - just compare result */
  1311.     if ( ! relative_url ){
  1312.         goto CLEAN_UP;
  1313.     }
  1314.  
  1315.     /* We can now figure out length of relative URL */
  1316.     new_length = XP_STRLEN(url_ptr) + (subdir_count*3) + 1;
  1317.     relative = (char *) XP_ALLOC(new_length);
  1318.     if ( !relative ){
  1319.         /* out of memory */
  1320.         Result = NET_URL_FAIL;
  1321.         goto CLEAN_UP;
  1322.     }
  1323.  
  1324.     *relative = '\0';
  1325.  
  1326.     /* Add "../" for each subdir left in the base */
  1327.     for ( i = 0; i < subdir_count; i++ ){
  1328.         XP_STRCAT(relative, "../");
  1329.     }
  1330.  
  1331.     /* Append the relative path that doesn't match */
  1332.     XP_STRCAT(relative, url_ptr);
  1333.  
  1334. CLEAN_UP:
  1335.     if ( relative_url ){
  1336.         if ( relative ){
  1337.             /* We allocced a relative URL - return it */
  1338.             *relative_url = relative;
  1339.         } else {
  1340.             /* For all other cases, return absolute URL */
  1341.             *relative_url = XP_STRDUP(absolute_url);
  1342.         }
  1343.     }
  1344.     XP_FREE(absolute_url);
  1345.  
  1346.     if( base_path ){
  1347.         XP_FREE(base_path);
  1348.     }
  1349.     if( base_host ){
  1350.         XP_FREE(base_host);
  1351.     }
  1352.     if( url_host ){
  1353.         XP_FREE(url_host);
  1354.     }
  1355.     return Result;
  1356. }
  1357.  
  1358. /* Extract the filename from a source URL 
  1359.  *  and construct add it the same path as 
  1360.  *  a base URL (replacing file in base)
  1361.  * BasePath should be an HTTP: or FILE: URL type,
  1362.  *  but we don't check it.
  1363.  * src_url may be a relative URL
  1364.  * If a pointer to a string is supplied,
  1365.  *  the new relative URL is returned
  1366.  *  (just the "file" portion of src_url)
  1367.  * Note: relative_url can be &src_url,
  1368.  *  thus replacing the source
  1369.  * Caller must XP_FREE the string(s)
  1370. */
  1371. char * NET_MakeTargetURL( char *base_url,
  1372.                           char *src_url,
  1373.                           char **relative_url )
  1374. {
  1375.     char *target = NULL;
  1376.     char *targ_ptr;
  1377.     char *src_ptr;
  1378.     char *relative_start;
  1379.     char *base_end;
  1380.     char *src_start;
  1381.     char *src_end = NULL;
  1382.     char *question;
  1383.     int   base_length;
  1384.     int   src_length;
  1385.     int   i;
  1386.  
  1387.     if( base_url == NULL || src_url == NULL ||
  1388.         XP_STRLEN(base_url) == 0 || 
  1389.         XP_STRLEN(src_url) == 0  ){
  1390.         return NULL;
  1391.     }
  1392.  
  1393.     /* Find where to concatenate the source
  1394.      *  we must have a slash or our base isn't valid!
  1395.     */
  1396.     base_end = XP_STRRCHR(base_url, '/');
  1397.     if ( !base_end ){
  1398.         return NULL;
  1399.     } 
  1400.     base_end++;
  1401.  
  1402.  
  1403.     /* Find start of source (usually filename) right after the last slash */
  1404.     src_start = XP_STRRCHR(src_url, '/');
  1405.     if (src_start){
  1406.         src_start++;
  1407.     } else {
  1408.         /* No slash, start at beginning */
  1409.         src_start = src_url;
  1410.     }
  1411.  
  1412.     /* Find end at neareast '#' or '?'  */
  1413.     src_end = XP_STRCHR(src_start, '#');
  1414.     question = XP_STRCHR(src_start, '?');
  1415.  
  1416.     if ( question &&
  1417.         ( !src_end || question < src_end ) ){
  1418.         src_end = question;
  1419.     }
  1420.  
  1421.     /* Calculate length of segments we will copy */
  1422.     if ( src_end ){
  1423.         src_length = src_end - src_start;
  1424.     } else {
  1425.         src_length = XP_STRLEN(src_start);
  1426.     }
  1427.     base_length = base_end - base_url;
  1428.  
  1429.     target = (char *) XP_ALLOC(base_length + src_length + 1);
  1430.     if ( target ){
  1431.         targ_ptr = target;
  1432.         src_ptr = base_url;
  1433.  
  1434.         /* Copy the base until we reach file part */
  1435.         for ( i = 0; i < base_length; i++ ){
  1436.             *targ_ptr++ = *src_ptr++;
  1437.         }
  1438.  
  1439.         /* Save the start of the new relative URL */
  1440.         relative_start = targ_ptr;
  1441.  
  1442.         /* Concatenate the source file */
  1443.         for ( i = 0; i < src_length; i++ ){
  1444.             *targ_ptr++ = *src_start++;
  1445.         }
  1446.         *targ_ptr = '\0';
  1447.  
  1448.         /* Supply the new relative URL if requested */
  1449.         if ( relative_url ) {
  1450.             /* Free existing string */
  1451.             if ( *relative_url ){
  1452.                 XP_FREE(*relative_url);
  1453.             }
  1454.             *relative_url = XP_STRDUP(relative_start);
  1455.         }
  1456.     }
  1457.  
  1458.     return target;
  1459. }    
  1460.  
  1461.  
  1462. PUBLIC char *
  1463. NET_UnEscape(char * str)
  1464. {
  1465.     (void)NET_UnEscapeCnt(str);
  1466.  
  1467.     return str;
  1468. }
  1469.  
  1470. /*******************************************************
  1471.  * Routines to parse the application/http-index-format
  1472.  */
  1473. #define MAX_200_FIELDS 50
  1474. #define ARRAY_GROWTH_RATE 50
  1475.  
  1476. #define UNKNOWN_TOKEN            0
  1477. #define FILENAME_TOKEN            1
  1478. #define CONTENT_LENGTH_TOKEN    2
  1479. #define LAST_MODIFIED_TOKEN        3
  1480. #define CONTENT_TYPE_TOKEN        4
  1481. #define FILE_TYPE_TOKEN            5
  1482. #define PERMISSIONS_TOKEN        6
  1483.  
  1484. struct _HTTPIndexParserData {
  1485.     NET_FileEntryInfo   ** data_list;
  1486.     int32                  data_list_length;
  1487.     int32                 number_of_files;
  1488.     char                  * line_buffer;
  1489.     int32                    line_buffer_size;
  1490.     char                  * text_user_message;
  1491.     char                  * html_user_message;
  1492.     char                 * base_url;
  1493.     int                      mapping_array[MAX_200_FIELDS];
  1494. };
  1495.  
  1496. PUBLIC HTTPIndexParserData *
  1497. NET_HTTPIndexParserInit()
  1498. {
  1499.     /* create and init parser data */
  1500.     HTTPIndexParserData * rv = XP_NEW(HTTPIndexParserData);
  1501.  
  1502.     if(!rv)
  1503.         return(NULL);
  1504.  
  1505.     XP_MEMSET(rv, 0, sizeof(HTTPIndexParserData));
  1506.     
  1507.     return(rv);
  1508. }
  1509.  
  1510. PUBLIC char *
  1511. strchr_in_buf(char *string, int32 string_len, char find_char)
  1512. {
  1513.     char *cp;
  1514.     char *end = string+string_len;
  1515.  
  1516.     for(cp = string; cp < end; cp++)
  1517.         if(*cp == find_char)
  1518.             return(cp);
  1519.  
  1520.     return(NULL);
  1521. }
  1522.  
  1523. /* return the next space or quote separated token */
  1524. PRIVATE char *
  1525. net_get_next_http_index_token(char * line)
  1526. {
  1527.     static char *next_token_start=NULL;
  1528.     char *start, *end;
  1529.     
  1530.     if(line == NULL)
  1531.         start = next_token_start;
  1532.     else
  1533.         start = line;
  1534.  
  1535.     if(start == NULL)
  1536.         return(NULL);
  1537.  
  1538.     while(XP_IS_SPACE(*start)) start++;
  1539.  
  1540.     if(*start == '"')
  1541.       {
  1542.         /* end of token is next quote */
  1543.         start++;
  1544.  
  1545.         end = start;
  1546.  
  1547.         while(*end != '\0' && *end != '"') end++;
  1548.       }
  1549.     else
  1550.       {
  1551.         /* end of token is next space */
  1552.         end = start;
  1553.  
  1554.         while(*end != '\0' && *end != ' ') end++;
  1555.       }
  1556.  
  1557.     if(*end == '\0')
  1558.         next_token_start = end;
  1559.     else
  1560.         next_token_start = end+1;
  1561.     *end = '\0';
  1562.  
  1563.     if(*start == '\0')
  1564.         return(NULL);
  1565.     else
  1566.         return(start);
  1567. }
  1568.  
  1569. PRIVATE int
  1570. net_parse_http_index_200_line(HTTPIndexParserData *obj, char *data_line)
  1571. {
  1572.     int32 position=0;
  1573.     char *token = net_get_next_http_index_token(data_line);
  1574.  
  1575.     while(token)
  1576.       {
  1577.         if(!strcasecomp("FILENAME", token))
  1578.           {
  1579.             obj->mapping_array[position] = FILENAME_TOKEN;
  1580.           }
  1581.         else if(!strcasecomp("CONTENT-LENGTH", token))
  1582.           {
  1583.             obj->mapping_array[position] = CONTENT_LENGTH_TOKEN;
  1584.           }
  1585.         else if(!strcasecomp("LAST-MODIFIED", token))
  1586.           {
  1587.             obj->mapping_array[position] = LAST_MODIFIED_TOKEN;
  1588.           }
  1589.         else if(!strcasecomp("CONTENT-TYPE", token))
  1590.           {
  1591.             obj->mapping_array[position] = CONTENT_TYPE_TOKEN;
  1592.           }
  1593.         else if(!strcasecomp("FILE-TYPE", token))
  1594.           {
  1595.             obj->mapping_array[position] = FILE_TYPE_TOKEN;
  1596.           }
  1597.         else if(!strcasecomp("PERMISSIONS", token))
  1598.           {
  1599.             obj->mapping_array[position] = PERMISSIONS_TOKEN;
  1600.           }
  1601.         else
  1602.           {
  1603.             obj->mapping_array[position] = UNKNOWN_TOKEN;
  1604.           }
  1605.  
  1606.         token = net_get_next_http_index_token(NULL);
  1607.         position++;
  1608.         if(position > MAX_200_FIELDS)
  1609.             break;
  1610.       }
  1611.  
  1612.     return(0);
  1613. }
  1614.  
  1615. PRIVATE int
  1616. net_parse_http_index_201_line(HTTPIndexParserData *obj, char *data_line)
  1617. {
  1618.     int32 position=0;
  1619.     char *token = net_get_next_http_index_token(data_line);
  1620.     NET_FileEntryInfo *file_struct;
  1621.  
  1622.     /* make sure there is enough space for the file
  1623.      * entry in the array
  1624.      */
  1625.     if(obj->data_list_length < obj->number_of_files+1)
  1626.       {
  1627.         /* malloc some more space for the array
  1628.          */
  1629.         if(obj->data_list)
  1630.             obj->data_list = (NET_FileEntryInfo **) XP_REALLOC(obj->data_list, 
  1631.                                                     (obj->data_list_length 
  1632.                                                     + ARRAY_GROWTH_RATE)
  1633.                                                     * sizeof(NET_FileEntryInfo*));
  1634.         else
  1635.             obj->data_list = (NET_FileEntryInfo **) XP_ALLOC(ARRAY_GROWTH_RATE 
  1636.                                                     * sizeof(NET_FileEntryInfo*));
  1637.         obj->data_list_length += ARRAY_GROWTH_RATE;
  1638.       }
  1639.  
  1640.     if(!obj->data_list)
  1641.         return(MK_OUT_OF_MEMORY);
  1642.  
  1643.     file_struct = NET_CreateFileEntryInfoStruct();
  1644.     if(!file_struct)
  1645.         return(MK_OUT_OF_MEMORY);
  1646.  
  1647.     while(token)
  1648.       {
  1649.         NET_UnEscape(token);
  1650.         switch(obj->mapping_array[position])
  1651.           {
  1652.             case UNKNOWN_TOKEN:
  1653.                 /* ignore unknown token types */
  1654.                 break;
  1655.  
  1656.             case FILENAME_TOKEN:
  1657.                 file_struct->filename = XP_STRDUP(token);
  1658.                 break;
  1659.  
  1660.             case CONTENT_LENGTH_TOKEN:
  1661.                 file_struct->size = atoi(token);
  1662.                 break;
  1663.  
  1664.             case LAST_MODIFIED_TOKEN:
  1665.                 file_struct->date = XP_ParseTimeString(token, TRUE);
  1666.                 break;
  1667.  
  1668.             case CONTENT_TYPE_TOKEN:
  1669.                 file_struct->content_type = XP_STRDUP(token);
  1670.                 break;
  1671.  
  1672.             case FILE_TYPE_TOKEN:
  1673.                 if(!strcasecomp(token, "FILE"))
  1674.                   {
  1675.                     file_struct->special_type = NET_FILE_TYPE;
  1676.                   }
  1677.                 else if(!strcasecomp(token, "DIRECTORY"))
  1678.                   {
  1679.                     file_struct->special_type = NET_DIRECTORY;
  1680.                   }
  1681.                 else if(!strcasecomp(token, "SYMBOLIC-LINK"))
  1682.                   {
  1683.                     file_struct->special_type = NET_SYM_LINK;
  1684.                   }
  1685.                 else if(!strcasecomp(token, "SYM-FILE"))
  1686.                   {
  1687.                     file_struct->special_type = NET_SYM_LINK_TO_FILE;
  1688.                   }
  1689.                 else if(!strcasecomp(token, "SYM-DIRECTORY"))
  1690.                   {
  1691.                     file_struct->special_type = NET_SYM_LINK_TO_DIR;
  1692.                   }
  1693.                 break;
  1694.  
  1695.             case PERMISSIONS_TOKEN:
  1696.                 if(XP_STRLEN(token) == 3)
  1697.                   {
  1698.                     file_struct->permissions = 0;
  1699.                     if(token[0] == 'R')
  1700.                         file_struct->permissions |= NET_READ_PERMISSION;
  1701.                     if(token[1] == 'W')
  1702.                         file_struct->permissions |= NET_WRITE_PERMISSION;
  1703.                     if(token[2] == 'E')
  1704.                         file_struct->permissions |= NET_EXECUTE_PERMISSION;
  1705.                   }
  1706.                 break;
  1707.  
  1708.             default:
  1709.                 XP_ASSERT(0);
  1710.                 break;
  1711.           }
  1712.         
  1713.  
  1714.         token = net_get_next_http_index_token(NULL);
  1715.         position++;
  1716.         if(position > MAX_200_FIELDS)
  1717.             break;
  1718.       }
  1719.  
  1720.     /* every struct must have a filename.  If it doesn't
  1721.      * destroy it
  1722.      */    
  1723.     if(!file_struct->filename || !*file_struct->filename)
  1724.         NET_FreeEntryInfoStruct(file_struct);
  1725.     else
  1726.         obj->data_list[obj->number_of_files++] = file_struct;
  1727.  
  1728.     return(0);
  1729. }
  1730.  
  1731.  
  1732. PUBLIC int
  1733. NET_HTTPIndexParserPut(HTTPIndexParserData *obj, char *data, int32 len)
  1734. {
  1735.     char *data_line, *new_line, *colon;
  1736.     int32 string_len;
  1737.     int32 line_code, status;
  1738.  
  1739.     if(!obj)
  1740.         return(MK_OUT_OF_MEMORY);
  1741.  
  1742.     /* buffer until we have a line */
  1743.     BlockAllocCat(obj->line_buffer, obj->line_buffer_size, data, len);
  1744.     obj->line_buffer_size += len;
  1745.     
  1746.     /* see if we have a line */
  1747.     while(((new_line = strchr_in_buf(obj->line_buffer, 
  1748.                                     obj->line_buffer_size, 
  1749.                                     CR)) != NULL)
  1750.           || ((new_line = strchr_in_buf(obj->line_buffer, 
  1751.                                     obj->line_buffer_size, 
  1752.                                     LF)) != NULL) )
  1753.       {
  1754.         if(new_line[0] == CR && new_line[1] == LF)
  1755.         {
  1756.             *new_line = '\0'; /* get rid of the LF too */
  1757.             new_line++;
  1758.         }
  1759.             
  1760.         /* terminate the line */
  1761.         *new_line = '\0';
  1762.  
  1763.         data_line = XP_StripLine(obj->line_buffer);
  1764.  
  1765.         /* get the line code */
  1766.         colon = XP_STRCHR(data_line, ':');
  1767.  
  1768.         if(!colon)
  1769.             goto end_of_line;
  1770.  
  1771.         *colon = '\0';
  1772.  
  1773.         line_code = atoi(XP_StripLine(data_line));
  1774.         
  1775.         /* reset data_line to the start of data after the colon */
  1776.         data_line = colon+1;
  1777.         
  1778.         status = 0;
  1779.  
  1780.         switch(line_code)
  1781.           {
  1782.             case 100:
  1783.                 /* comment line, ignore */
  1784.                 break;
  1785.  
  1786.             case 101:
  1787.                 StrAllocCat(obj->text_user_message, data_line);
  1788.                 StrAllocCat(obj->text_user_message, "\n");
  1789.                 break;
  1790.  
  1791.             case 102:
  1792.                 StrAllocCat(obj->html_user_message, data_line);
  1793.                 StrAllocCat(obj->html_user_message, "\n");
  1794.                 break;
  1795.  
  1796.             case 200:
  1797.                 status = net_parse_http_index_200_line(obj,    data_line);
  1798.                 break;
  1799.  
  1800.             case 201:
  1801.                 status = net_parse_http_index_201_line(obj,    data_line);
  1802.                 break;
  1803.  
  1804.             case 300:
  1805.                 StrAllocCat(obj->base_url, data_line);
  1806.                 break;
  1807.  
  1808.             default:
  1809.                 /* fall through and ignore the line */
  1810.                 break;
  1811.           }
  1812.  
  1813.         if(status < 0)
  1814.             return(status);
  1815.  
  1816. end_of_line:
  1817.         /* remove the parsed line from obj->line_buffer */
  1818.         string_len = (new_line - obj->line_buffer) + 1;
  1819.         XP_MEMCPY(obj->line_buffer, 
  1820.                   new_line+1, 
  1821.                   obj->line_buffer_size-string_len);
  1822.         obj->line_buffer_size -= string_len;
  1823.  
  1824.       }
  1825.         
  1826.     return(0);
  1827. }
  1828.  
  1829. PUBLIC int
  1830. NET_HTTPIndexParserSort(HTTPIndexParserData *data_obj, int sort_method)
  1831. {
  1832. /* @@@@@@ soon */
  1833.     return(0);
  1834. }
  1835.  
  1836. PUBLIC char *
  1837. NET_HTTPIndexParserGetTextMessage(HTTPIndexParserData *data_obj)
  1838. {
  1839.     return(data_obj->text_user_message);
  1840. }
  1841.  
  1842. PUBLIC char *
  1843. NET_HTTPIndexParserGetHTMLMessage(HTTPIndexParserData *data_obj)
  1844. {
  1845.     return(data_obj->html_user_message);
  1846. }
  1847.  
  1848. PUBLIC char *
  1849. NET_HTTPIndexParserGetBaseURL(HTTPIndexParserData *data_obj)
  1850. {
  1851.     return(data_obj->base_url);
  1852. }
  1853.  
  1854. PUBLIC NET_FileEntryInfo *
  1855. NET_HTTPIndexParserGetFileNum(HTTPIndexParserData *data_obj, int32 num)
  1856. {
  1857.     if(num >= data_obj->number_of_files || num < 0)
  1858.         return(NULL);
  1859.  
  1860.     return(data_obj->data_list[num]);
  1861. }
  1862.  
  1863. PUBLIC int32
  1864. NET_HTTPIndexParserGetTotalFiles(HTTPIndexParserData *data_obj)
  1865. {
  1866.     return(data_obj->number_of_files);
  1867. }
  1868.  
  1869. PUBLIC void
  1870. NET_HTTPIndexParserFree(HTTPIndexParserData *obj)
  1871. {
  1872.     int32 i;
  1873.  
  1874.     if(!obj)
  1875.         return;
  1876.  
  1877.     /* FREE file data structures and array */
  1878.     for(i=0; i<obj->number_of_files; i++)
  1879.         NET_FreeEntryInfoStruct(obj->data_list[i]);
  1880.  
  1881.     FREEIF(obj->data_list);  /* array */
  1882.     FREEIF(obj->line_buffer);
  1883.     FREEIF(obj->text_user_message);
  1884.     FREEIF(obj->html_user_message);
  1885.     FREE(obj);
  1886. }
  1887.  
  1888.  
  1889. #ifdef PROFILE
  1890. #pragma profile off
  1891. #endif
  1892.