/*
 * $Log: url,v $
 * Revision 1.20  1998/12/05 15:43:56  joseph
 * Schemes are now converted to lower case before adding to lists
 *
 * Revision 1.19  1998/09/28 15:56:18  joseph
 * Changed to delay loading of mimemap module to the start of a fetch
 *
 * Revision 1.18  1998/09/02 19:21:22  joseph
 * Replaced internal mimemap stuff with calls to MimeMap module
 *
 * Revision 1.17  1998/08/30 18:38:14  joseph
 * Removed debug_enable()'s / debug_disable()'s
 *
 * Revision 1.16  1998/08/30 18:31:48  joseph
 * Fetching of inline images may be specified more precisely
 *
 * Revision 1.15  1998/08/30 13:09:59  joseph
 * Fetching ordered is now more defined - pages closer to the root, and
 * actual pages (as opposed to inlines) at the same line level are favoured.
 *
 * Revision 1.14  1998/08/11 20:35:49  joseph
 * Added url_clear() to clear URLs from all the lists at the end of a fetch
 *
 * Revision 1.13  1998/08/09 16:18:54  joseph
 * Fixed status window waiting count not getting updated at right time
 *
 * Revision 1.12  1998/08/09 15:07:00  joseph
 * Added include/exclude lists for URLs
 *
 * Revision 1.11  1998/07/01 21:10:58  joseph
 * Added status window
 * Fixed persistent connections
 * Fixed magtags to only count processed url references
 * Fixed proxy_forunqualified, again.
 * Fixed leading/trailing spaces in urls...
 *
 * Revision 1.10  1998/06/14 21:24:37  joseph
 * Moved relative url resolver from rewrite / http -> misc and rewrote
 * Fixed rewrite leaving []'s all over log file.
 * Fixed misc_urltofilename barfing on url's like http://wiggle
 * Lessened processor time when fetching pages with _lots_ of links in them.
 * Make FetchError open/close at start/end of fetch like FetchLog
 * Fixed handling of ftp urls, with & without proxy set.
 * Fixed bugs in code to set fetchportion
 * URLs now moved to fetch list when fetched (whether sucessful or not)
 * Log file now says '[http]' or '[ftp]' for real link, as appropriate.
 * Recalculates fetch portion for redirected urls
 * Fixed handling of proxy for ftp (wasn't always using proxy)
 * url.c now uses 'times 2' allocator / deallocator.
 * url_handled corrected for ftp (returns true only if proxy set)
 * closes log file when fetch aborted.
 *
 * Revision 1.9  1998/06/06 21:44:56  joseph
 * Added ability to not fetch frames / inline images in fetch / config
 * wimpc_menuread moved to wimpclib
 * Now fades individual fetches when started, unfades all at end of fetch
 *
 * Revision 1.8  1998/06/05 22:55:17  joseph
 * URLs now moved to 'FETCHED' once they are fetched
 * Front end made proper
 * Added multiple fetch files + a menu in frontend
 * Fixed bugs with links containing url with spaces before them
 * Combined both makefilenames into one in misc.c
 * Added readtag and openfetch to http.
 * Keep FetchLog open for duration of fetch
 * Logfile now contains links to the orignal pages too
 * Added url_handled, returns true if the url is one we know how to fetch
 * Added shift to fetch without rewriting, and adjust to dump all flex
 * lists to files. (#define URLDUMP)
 * Frontend animation now updates every 0.5 secs, and has new rewrite icon
 * Added stopping / aborting of fetch / rewrite
 *
 * Revision 1.7  1998/05/28 22:26:31  joseph
 * Altered to compile with debugging changes
 *
 * Revision 1.6  1998/05/28 19:41:11  joseph
 * Changed all printf()'s to debug_printf(())'s
 *
 * Revision 1.5  1998/05/27 22:37:27  joseph
 * Made mimetype_add static
 *
 * Revision 1.4  1998/04/05 11:25:15  jogu
 * Added updating of fetchportion/linkdepth when a url is found higher in the
 * tree than we previously found it
 *
 * Revision 1.3  1998/03/25 17:58:02  jogu
 * Added fetch portion
 *
 * Revision 1.2  1998/03/21 20:33:11  jogu
 * Added linkdepth.
 *
 * Revision 1.1.1.1  1997/12/29 14:37:55  jogu
 * WebGet Initial CMS Ver
 *
 */


#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define BOOL int
#include "flex.h"
#undef BOOL

#include <assert.h>
#include "swis.h"

#include "my_string.h"
#include "debug.h"
#include "macros.h"
#include "config.h"
#include "http.h"
#include "ruleset.h"
#include "file.h"

#define URL_INTERNALS
#include "url.h"
#undef URL_INTERNALS

#define NO_LISTS SIZEOF_LISTS_ENUM

/*static flex_ptr lists[3] {flex_found = &found_list,
                flex_pending = &pending_list,
                flex_fetched = &fetched_list; */


/* url_listsize
 *
 * holds the amount of memory each list is using.
 * NB. This will always be <= flex_size() on the list
 */
static size_t url_listsize[SIZEOF_LISTS_ENUM] = { 0, 0, 0/*, 0*/ };


#define WORDALIGN(x) ( (x+3) & ~3 )

/* allocation technique:
 *
 * flex_size and flex_midextend may NOT be called from other procs.
 * Others procs call flex_ensureroom(...) to extend blocks / retract blocks?
 */
/* --------------------------- flex_midextend -----------------------------
 * Description:   Extend or truncate store, at any point.
 *
 * Parameters:    flex_ptr -- pointer to allocated store
 *                int at -- location within the allocated store
 *                int by -- extent
 * Returns:       0 == failure, 1 == success
 * Other Info:    If by is +ve, then store is extended, and locations above
 *                "at" are copied up by "by".
 *                If by is -ve, then store is reduced, and any bytes beyond
 *                "at" are copied down to "at"+"by".
 *
 */
static int url_midextend( URL_LISTS where, int at, int by )
{
  int cursize = flex_size( (flex_ptr) &lists[where] );
  int needed  = url_listsize[ where ] + by;

  if ( at < 0 || at > cursize )
    return 0;

  if ( by < 0 && (-by) > at )
    return 0;

  if ( by == 0 ) return 1;

  /* do we need to claim more memory or not? */
  if ( needed > cursize )
  {
    int alloc = cursize * 2;
    if ( alloc < needed ) alloc = needed;
    if ( alloc < 512    ) alloc = 512;

//    debug_printf(("Extending %d by %d at %d. Changing block size to %d.",where,by,at,alloc));
    if ( !flex_extend( (flex_ptr) &lists[where], alloc ) ) return 0;
  }
  else
  {
//    debug_printf(("Extending %d by %d at %d. Sufficient memory present.",where,by,at));
  }

  /* have memory now, just move memory inside current block */
  if ( at != url_listsize[where] )
    memmove( lists[where] + at + by, lists[where] + at, url_listsize[where] - at );
//  else
//    debug_printf(("Skipped memmove."));
  url_listsize[where] += by;
//  debug_printf(("Succeeded, urlsize now %d\n",url_listsize[where]));

  /* free memory if the size has been reduced */
  cursize = flex_size( (flex_ptr) &lists[where] );
  /* this shouldn't be strictly necessary, but I'm erring on the side of caution */

  if ( url_listsize[where] * 2 < cursize )
  {
    int alloc = cursize / 2;
    if ( alloc < 512 ) alloc = 512; /* enforce minimum size */
    if ( url_listsize[where] == 0 ) alloc = 0; /* unless the list is empty */
    if ( alloc != cursize )
      flex_extend( (flex_ptr) &lists[where], alloc ); /* shouldn't fail, and we don't really care if it does */
  }

  return 1;
}



int url_init(void)
{
  int x;
  /* initialise the found, pending, fetched & mimetype lists */

  memset( url_listsize, 0, sizeof(size_t) * SIZEOF_LISTS_ENUM );

  for ( x=0; x < NO_LISTS; x++ )
    if ( !flex_alloc( (flex_ptr) &lists[x], 0 ) ) return -1;

  return 0;
}


void url_clear( void )
{
  /* set the found, pending & fetched lists to zero length (not mimetypes!) */

  flex_extend( (flex_ptr) &lists[FOUND], 0 );
  url_listsize[FOUND] = 0;
  flex_extend( (flex_ptr) &lists[PENDING], 0 );
  url_listsize[PENDING] = 0;
  flex_extend( (flex_ptr) &lists[FETCHED], 0 );
  url_listsize[FETCHED] = 0;
}


/* There is no delete function. We do not need a delete function.
   Removing a URL is done by moving it to fetched */

/* Must lower case scheme and remove a reference before calling this function */
static int url_present(URL_LISTS where, const char *url)
{
  /* we're not allowing flex to budge, and we're not calling flex functions anywhere.
     we can keep local ptr's which point inside flex blocks */
  int strsize = url_strsize[where];
  char *ptr, *end;
  int len = url_listsize[where];

  ptr = lists[where];
  end = ptr + len;

  while (ptr<end)
  {
    len = strlen(ptr + strsize) + 1; /* read length of string in this record */

    if (!memcmp(url, ptr+strsize, len))  return 1;  /* Matches */

    ptr+= WORDALIGN(strsize + len);
  }
  return 0;
}

/* returns 0 if the type is one we can handle (http or ftp)
 *
 */
int url_handled( const char *url )
{
  int len = strlen( url );

  if ( my_strstarts(url, len, "http:") )
    return 0; /* we can always handle http */
  if ( my_strstarts(url, len, "ftp:") )
  {
    if ( *config_proxyname[1] )
      return 0; /* ftp & ftp proxy set, can be handled */

    return 1; /* ftp and not proxy set. Can't be handled */
  }

  return -1; /* not http or ftp */
}


/* NB. Can't call this with a url stored in a flex block!! */
int url_addto( URL_LISTS where, char *url, int linkdepth, int fetchportion,
               int noinlineimages, int inlineframes, int ruleset, eval_inline inline, int act_linkdepth,
               int imagefetchportion)
{
  /* adds url to found list, only if it isn't in fetched / pending */
  int strsize = url_strsize[where];
  char *ptr;
  int len = strlen(url) + 1, size = url_listsize[where];
  char urlstore[ URL_MAXLEN ];

  if ( (ptr = strchr(url,'#')) != NULL )
  {
    /* There's an internal reference thingy. Remove it... */
    *ptr=0;
    len = strlen(url) + 1;
  }

  for ( ptr = url; *ptr && isscheme( *ptr ); *ptr = tolower( *ptr ), ptr++ ); /* lower case scheme */

  if ( url_present( FETCHED, url ) )
    return 0; /* Already fetched url */

  {
    url_t *record = url_getinfo(PENDING, url);
    URL_LISTS location=PENDING;
    if (!record) { record = url_getinfo(FOUND, url); location=FOUND; }
    if (record)
    {
      if (location==where || ( location==PENDING && where==FOUND ) )
      {
        /* Already in this list. Update link level? */
        if ( record->linkdepthtogo < linkdepth )
        {
          record->linkdepthtogo = linkdepth;
          record->act_linkdepth = act_linkdepth;
        }
        if ( record->fetchportion > fetchportion ) record->fetchportion = fetchportion;
        if ( record->imagefetchportion > imagefetchportion )
          record->imagefetchportion = imagefetchportion;
        if ( !noinlineimages ) record->noinlineimages = 0;
        if ( inlineframes ) record->inlineframes = 1;
        /* ruleset?? */
        return 0;
      }
      if ( (location==FOUND && where==PENDING) || where==FETCHED )
      {
        /* url is either in FOUND and we want to move it to PENDING, or it's somewhere else
           and we want to add it to fetched - move url to 'where' */
        /* remove URL from it's present location. Note, if 'url' is inside a flex block, we corrupt it */
        *urlstore = 0;
        strncat( urlstore, url, sizeof(urlstore) - 1 );
        url = urlstore;
        if ( record->ruleset != -1 )
          ruleset_unuse( record->ruleset );
        url_remove( location, url );
        len = strlen( url ) + 1;
/*        if ( location == PENDING )
          http_waiting--; */ /* url is removed from waiting list when we start fetching it */


      }
    }
  }

  /* The url isn't already present in the required list, add it */

  if ( !url_midextend( where, size, WORDALIGN(strsize + len) ) )
  {
    E_REPORT("Out of memory trying to add url");
    return -1;
  }

  ptr = lists[where] + size;
  memset(ptr, 0, strsize); /* initialise any structure to 0 */
  memcpy(ptr+strsize, url, len);

  if ( where != FETCHED )
  {
    url_t *x = (url_t *) ptr;
    x->linkdepthtogo      = linkdepth;
    x->act_linkdepth      = act_linkdepth;
    x->fetchportion       = fetchportion;
    x->imagefetchportion  = imagefetchportion;
    x->noinlineimages     = ( noinlineimages != 0 );
    x->inlineframes       = ( inlineframes != 0 );
    x->ruleset            = ruleset;
    x->inline             = inline;
    x->filetype           = -1;
  }
  if ( where == FETCHED )
    http_fetched++;
  else if ( where == PENDING )
    http_waiting++;

  if ( ruleset != -1 && where != FETCHED )
    ruleset_use( ruleset );

  return 0;
}


int url_remove(URL_LISTS where, const char *url)
{
  /* scan whole list, remove any occurence of this url */
  /* you can't add a url twice, so we can stop when we find the first occurence */
  int strsize = url_strsize[where];
  char *ptr, *end;
  int len = strlen(url) + 1, size = url_listsize[ where ];

  ptr = lists[where];
  end = ptr + size;

  while (ptr < end)
  {
    len = strlen( ptr + strsize ) + 1; /* read length of string in this record */
    if ( !memcmp( url, ptr+strsize, len ) )
    {
      if ( !url_midextend( where, ptr-lists[where] + WORDALIGN(strsize+len), -WORDALIGN(strsize+len)) )
        return -1;
      return 1;
    }
    ptr+= WORDALIGN( strsize + len );
  }
  return 0;
}



int url_promote(URL_LISTS where, const char *url)
{
  /* promote url to top of list (pending / found) */
  int strsize = url_strsize[where];
  char *ptr, *end;
  int len = strlen(url) + 1, size = url_listsize[where];

  ptr = lists[where];
  end = ptr + size;

  while (ptr < end)
  {
    len = strlen(ptr + strsize) + 1; /* read length of string in this record */
    if ( !memcmp(url, ptr+strsize, len) )
    {
      int offset = ptr - lists[where]; /* offset to this url */
      len = WORDALIGN(len + strsize);
      /* okay, this url matches. lets move the start down with a flex_midextend,
         then memcpy the url that's moving, then 'flex_midcontract' the space it was in */

      if ( !url_midextend( where, 0, len ) ) /* make space at start */
        return -1;

      memmove(lists[where], lists[where] + offset + len, len); /* move url. NB - offset is larger now */

      if ( !url_midextend( where, offset+len, -len ) )
        return -1;

      return 1;
    }
    ptr+= WORDALIGN(strsize + len);
  }
  return 0;
}

/* NB. The pointers these return is only good immediately following the call */
/* loop through list, find first url that isn't fetching atm. */

/* loop through till we find a not fetching url */
url_t *url_getnext(URL_LISTS where)
{
  int strsize = url_strsize[where];
  int len = url_listsize[where];
  char *ptr = lists[where], *end = ptr + len;
  url_t *url;
  int linkdepth = -1;
  url_t *found = NULL;
  eval_inline found_inline = inline_not; /* default val doesn't matter */

  assert(where!=FETCHED); /* Can't find a new url to fetch in the list of url's we've already fetched.... */

  while (ptr < end)
  {
    len = strlen(ptr + strsize) + 1; /* read length of string in this record */
    url = (url_t *) ptr;

    if ( !url->fetching && (url->act_linkdepth < linkdepth || linkdepth == -1 ||
        (url->act_linkdepth == linkdepth && url->inline < found_inline ) ) )
    {
      if ( url->act_linkdepth == 0 && url->inline == inline_not )
      {
        debug_printf(("Selected url at depth %d, inline = %d. %s\n", url->act_linkdepth, url->inline,ptr+strsize));
        return url;
      }
      found = url;
      found_inline = url->inline;
      linkdepth = url->act_linkdepth;
    }

    ptr+= WORDALIGN(strsize + len);
  }
  if ( found )
  {
    debug_printf(("Selected url at depth %d, inline = %d. %s\n", found->act_linkdepth, found_inline,
                  (char*)found+strsize));
  }
  return found;
}


url_t *url_getinfo(URL_LISTS where, const char *url)
{
  int strsize = sizeof(struct url_str), len = url_listsize[where];
  char *ptr = lists[where], *end = ptr + len;

  assert(where==PENDING || where==FOUND);

  while (ptr < end)
  {
    len = strlen(ptr + strsize) + 1; /* read length of string in this record */

    if (!memcmp(url, ptr+strsize, len))
      return (url_t *) ptr;

    ptr+= WORDALIGN(strsize + len);
  }
  return 0;
}



#if 0

/* routine to add a mimetype.
   Mimetype's are kept in alphabetic order, with any application/ going after application/zip, so we
   can search linearly.
   Each type is given a unique reference, starting from zero.

   We'll store the list as:

   structure
   majortype (string)
   minortype (string)
   */

/* Lookup a mimetype, return it */
mimetype_t *mimetype_lookup(char *type)
{
  int strsize = sizeof(mimetype_t), len = url_listsize[ MIMETYPES ];
  char *ptr = lists[MIMETYPES], *end = ptr + len, *minor;

  if ( (minor=strchr(type,'/')) == NULL ) return NULL;
  *minor++=0; /* Chop into major / minor type */

  /* Search through. Return first either exact match or matching majortype with no minor */
  while (ptr < end)
  {
    int len1 = strlen(ptr + strsize) + 1; /* read length of string in this record */
    int len2 = strlen(ptr + strsize + len1) + 1;
    int majormatch = strcmp(type, ptr+strsize);

    if (majormatch < 0) { *--minor='/';return NULL; } /* Not found */

    if (majormatch==0)
    {
      int minormatch = strcmp(minor, ptr+strsize+len1);

      if (minormatch==0 || !*(ptr+strsize+len1))
      {
        *--minor='/';
        return (mimetype_t *) ptr;
      }
    }
    ptr+= WORDALIGN(strsize + len1 + len2);
  }
  *--minor='/';
  return NULL; /* Not found */
}

/* Lookup a mimetype, return it */
mimetype_t *mimetype_lookupbyref(int ref)
{
  int strsize = sizeof(mimetype_t), len = url_listsize[ MIMETYPES ];
  char *ptr = lists[MIMETYPES], *end = ptr + len;

  /* Search through. Return first either exact match or matching majortype with no minor */
  while (ptr < end)     3
  {
    int len1 = strlen(ptr + strsize) + 1; /* read length of string in this record */
    int len2 = strlen(ptr + strsize + len1) + 1;
    if ( ((mimetype_t *) ptr)->ref == ref )
      return (mimetype_t *) ptr;
    ptr+= WORDALIGN(strsize + len1 + len2);
  }
  return NULL; /* Not found */
}


static int mimetype_add(char *type, int ftype, int fetch)
{
  static unsigned int ref = 1; /* start of ref's at 1 */
  URL_LISTS where = MIMETYPES;
  int strsize = url_strsize[where];
  int len = strlen(type) + 1;
  int offset;
  char *ptr,*minor;
  int update = 0; /* Set to true if we're updating an already present record */
  mimetype_t *q;

  if ( (minor=strchr(type,'/')) == NULL ) return -1;
  *minor++=0; /* Chop into major / minor type */

  /* Search for where we are going to insert this type */
  {
    int len = url_listsize[ MIMETYPES ];
    char *ptr = lists[where], *end = ptr + len;

    offset=len; /* Preset to inserting at the end of the list! */

    while (ptr < end)
    {
      int len1 = strlen(ptr + strsize) + 1; /* read length of string in this record */
      int len2 = strlen(ptr + strsize + len1) + 1;
      int majormatch = strcmp(type, ptr+strsize);

      if ( majormatch == 0 )
      {
        int minormatch = strcmp(minor, ptr+strsize+len1);
        /* major type matches - check minor */
        /* If minor is greater than ours, insert here.
           If it's less, carry on.
           If it's the same, we're just going to update the record  */
        if (minormatch<=0)
        {
          if (minormatch==0)
            update=1;
          else if (!*minor)
          {
            ptr+= WORDALIGN(strsize + len1 + len2);
            continue; /* make sure we insert a empty minor entry last */
          }
          offset = ptr - lists[where];
          break;
        }
      }
      else if (majormatch<0)
      {
          /* major type doesn't match - is it less or more than what we're looking for ? */
          offset = ptr - lists[where];
          break; /* insert before this one */
      }
      ptr+= WORDALIGN(strsize + len1 + len2);
    }
  }

  /* Expand flex block to make space - only if we're creating a new record */
  if ( !update )
    if ( !url_midextend( where, offset /* offset to insert at */, WORDALIGN(strsize + len) ) )
      return -1;

  /* Initialise structure to zero and copy in info */

  ptr = lists[where] + offset;
  memset(ptr, 0, strsize); /* initialise any structure to 0 */

  memcpy(ptr+strsize, type, len);

  q = (mimetype_t *) ptr;

  q->ref      = ref++;
  q->filetype = ftype;
  q->fetch    = (fetch != 0);

  return 0; /* success */
}



int mimetype_load(const char *file)
{
  char buffer[100];
  char *notfin = (char *) 1;
  FILE *f=fopen(file,"r");
  if (!f) return -1;

  while (fgets(buffer, sizeof(buffer), f) && notfin)
  {
    char *x,*buf,*ptr = strchr(buffer,'\n');

    buf=my_strstrip(buffer);
    if (*buf && *buf!='#' && *buf!=';' && (x=strchr(buf,' '))!=NULL )
    {
      *x++=0;
      /* buf now points at a filetype (in hex), and x to the content-type */
      if (strchr(x,'/'))
        if (mimetype_add(x, my_hexatoi(buf), 1) < 0) return -1;
    }

    if (!ptr)
      /* suck up rest of overlong lines */
      while ( (notfin = fgets(buffer, sizeof(buffer), f)) != NULL && !strchr(buffer, '\n') );
  }
  fclose(f);
  return 1;
}

#endif


#define SWI_MimeMap_Translate 0x50b00

#define MIME_FILETYPE     0x00
#define MIME_FILETYPENAME 0x01
#define MIME_MIMETYPE     0x02
#define MIME_EXTNAME      0x03
static const char mimemap_error[] = "Unable to load MimeMap module";
static const char var[] = "WebGet$MimeMap";


static int mimemap_load( void )
{
  const char syspath[] = "System:Modules.Network.MimeMap";
  const char inetpath[] = "Inet:rm.MimeMap";
  const char mimemap_var[] = "Inet$MimeMappings";
  const char default_map[] = "InetDBase:MimeMap";
  char cmd[256];
  _kernel_oserror *e = NULL;
  char *val;

  strcpy( cmd, "%RMLoad " );

  /* find out where the module is */
  if ( file_exists( syspath ) )
    strcat( cmd, syspath );
  else if ( file_exists( inetpath ) )
    strcat( cmd, inetpath );
  else
  {
    E_REPORT("Unable to find MimeMap module - Please run !ModInst");
    return -1;
  }

  /* check Inet$MimeMappings is set */
  val = getenv( mimemap_var );
  if ( !val )
  {
    if ( !file_exists( default_map ) )
    {
      E_REPORT( "Could not locate mimemappings file - Please ensure your internet "
                "suite has been run, and that you've run !ModInst" );
      return -1;
    }

    e = _kernel_setenv( mimemap_var, default_map );
  }

  if ( !e )
    e = _swix( OS_CLI, _IN(0), cmd ); /* load module */

  if ( !e )
    e = _kernel_setenv( var, "Y" );

  if ( !e )
  {
    e = _swix( OS_CLI, _IN(0), "%RMEnsure MimeMap 0.10 Set WebGet$MimeMap N" );
    val = getenv( var );
    if ( !val || *val == 'N' )
    {
      E_REPORT("You need MimeMap 0.10 or later - please run !ModInst");
      return -1;
    }
  }

  if ( e )
  {
    E_CHECK( e );
    E_REPORT( mimemap_error );
    return -1;
  }

  return 0;
}


/* loads mimemap module */
int mimetype_init( void )
{
  _kernel_oserror *e = NULL;
  char *val;
  int ret = 0;

  e = _kernel_setenv( var, "Y" );

  if ( !e )
    e = _swix( OS_CLI, _IN(0), "%RMEnsure MimeMap 0.10 Set WebGet$MimeMap N" );

  if ( e )
  {
    E_CHECK( e );
    E_REPORT( mimemap_error );
    return -1;
  }

  val = getenv( var );
  if ( !val || *val == 'N' )
    ret = mimemap_load();

  _kernel_setenv( var, NULL ); /* delete variable */

  return ret;
}

int mimetype_getfiletype( const char *mimetype )
{
  int ftype;
  _kernel_oserror *e;

  e = _swix( SWI_MimeMap_Translate, _INR(0,3) | _OUT(3), MIME_MIMETYPE, mimetype, MIME_FILETYPE, 0, &ftype );
  if ( e )
  {
    E_CHECK( e );
    return 0xfaf;
  }

  return ftype;
}


void url_dump(URL_LISTS where, const char *file)
{
  FILE *f = fopen(file,"wb");
  if ( !f ) { debug_printf(("File error!\n")); return; }

  fwrite( lists[where], 1, url_listsize[where], f );

  fclose( f );
}

