VOS.VOSDiscussionsDETAPI

  • Topic
  • Discussion
  • VOS.VOSDiscussionsDETAPI(Last) -- DAVWikiAdmin? , 2017-06-29 07:35:07 Edit WebDAV System Administrator 2017-06-29 07:35:07

    --  
    --  $Id: DET_nntp.sql,v 1.2 2007/03/28 10:48:51 source Exp $
    --
    --  This file is part of the OpenLink Software Virtuoso Open-Source (VOS)
    --  project.
    --  
    --  Copyright (C) 1998-2006 OpenLink Software
    --  
    --  This project is free software; you can redistribute it and/or modify it
    --  under the terms of the GNU General Public License as published by the
    --  Free Software Foundation; only version 2 of the License, dated June 1991.
    --  
    --  This program is distributed in the hope that it will be useful, but
    --  WITHOUT ANY WARRANTY; without even the implied warranty of
    --  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    --  General Public License for more details.
    --  
    --  You should have received a copy of the GNU General Public License along
    --  with this program; if not, write to the Free Software Foundation, Inc.,
    --  51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
    --  
    --  
    
    use DB
    ;
    
    create function "nntp_FIXNAME" (in mailname any) returns varchar
    {
      return
        replace (
          replace (
            replace (
              replace (
                replace (
                  replace (
                    replace (
                      replace (mailname, '/', '_'), '\\', '_'), ':', '_'), '+', '_'), '\"', '_'), '[', '_'), ']', '_'), '\'', '_');
    }
    ;
    
    create procedure "nntp_PARSE_SERVER_NAME"(in fullname varchar, out server varchar, out port integer)
    {
      declare pos integer;
      pos := strrchr(fullname, '_');
      server := subseq(fullname, 0, pos);
      port := atoi(subseq(fullname, pos + 1));
    }
    ;
    
    create function "nntpf_display_message_text2"(in _text varchar, in ct varchar := 'text/plain', in ses any)
    {
       _text := nntpf_replace_at (_text);
       if (ct = 'text/plain')
         http ('<pre class="artbody"> <br/>', ses);
       else
         http ('<div class="artbody">', ses);
    
       http (_text, ses);
    
       if (ct = 'text/plain')
         http ('</pre>', ses);
       else
         http ('</div>', ses);
    }
    ;
    
    create function "nntp_COMPOSE_HTML_NAME" (in title varchar, in id varchar) returns varchar
    {
      if (title is null or title = '')
        return "nntp_FIXNAME"(sprintf('%s', id));
      return "nntp_FIXNAME"(sprintf('%s %s', title, id));
    }
    ;
    
    create function "nntp_COMPOSE_COMMENTS_NAME" (in title varchar, in id varchar) returns varchar
    {
      if (title is null or title = '')
        return "nntp_FIXNAME"(sprintf('%s Comments', id));
      return "nntp_FIXNAME"(sprintf('%s %s Comments', title, id));
    }
    ;
    
    create procedure "nntp_PARSE_HTML_NAME" (in fullname varchar, out title varchar, out id varchar)
    {
      declare pos integer;
      pos := strrchr(fullname, ' ');
      if (pos is NULL)
        id := fullname;
      else
      {
        title := subseq(fullname, 0, pos);
        id := subseq(fullname, pos + 1);
      }
    }
    ;
    
    create procedure "nntp_PARSE_COMMENTS_NAME" (in fullname varchar, out title varchar, out id varchar)
    {
      declare pos integer;
      declare real_part, comment_part varchar;
      pos := strrchr(fullname, ' ');
      comment_part := subseq(fullname, pos + 1);  
      if (comment_part = 'Comments')
      {
        real_part := subseq(fullname, 0, pos);
        pos := strrchr(real_part, ' ');
        if (pos is NULL)
          id := real_part;
        else
        {
          title := subseq(real_part, 0, pos);
          id := subseq(real_part, pos + 1);
        }    
      }
    }
    ;
    
    create function "nntp_CHANNEL_DESC_NAMES" () returns any
    {
      return vector ('atom.xml', 'foaf.xml', 'index.ocs', 'index.opml', 'index.rdf', 'rss.xml', 'xbel.xml');
    }
    ;
    
    create function "nntp_ACCESS_PARAMS" (in detcol_id any, out access varchar, out gid integer, out uid integer)
    {
      declare access_tmp varchar;
      whenever not found goto ret;
      access := '100000000NN';
      gid := http_nogroup_gid ();
      uid := http_nobody_uid ();
      if (isinteger (detcol_id))
      {
        select COL_PERMS, COL_GROUP, COL_OWNER into access_tmp, gid, uid from WS.WS.SYS_DAV_COL where COL_ID = detcol_id;
      }
      access[0] := access_tmp[0];
      access[1] := access_tmp[1];
    --  access[3] := access_tmp[3];
    ret:
      ;
    }
    ;
    
    --| This matches DAV_AUTHENTICATE (in id any, in what char(1), in req varchar, in a_uname varchar, in a_pwd varchar, in a_uid integer := null)
    --| The difference is that the DET function should not check whether the pair of name and password is valid; the auth_uid is not a null already.
    create function "nntp_DAV_AUTHENTICATE" (in id any, in what char(1), in req varchar, in auth_uname varchar, in auth_pwd varchar, in auth_uid integer)
    {
      -- dbg_obj_princ ('nntp_DAV_AUTHENTICATE (', id, what, req, auth_uname, auth_pwd, auth_uid, http_dav_uid(), ')');
      if (auth_uid < 0)
        return -12;
      if (not ('110' like req))
      {
        --dbg_obj_princ ('a_uid2 is ', auth_uid, ', id[3] is ', id[3], ' mismatch');
        return -13;
      }
      if ((auth_uid <> id[3]) and (auth_uid <> http_dav_uid()))
      {
        --dbg_obj_princ ('a_uid is ', auth_uid, ', id[3] is ', id[3], ' mismatch');
        return -13;
      }
      return auth_uid;
    }
    ;
    
    --| This exactly matches DAV_AUTHENTICATE_HTTP (in id any, in what char(1), in req varchar, in can_write_http integer, inout a_lines any, inout a_uname varchar, inout a_pwd varchar, inout a_uid integer, inout a_gid integer, inout _perms varchar) returns integer
    --| The function should fully check access because DAV_AUTHENTICATE_HTTP do nothing with auth data either before or after calling this DET function.
    --| Unlike DAV_AUTHENTICATE, user name passed to DAV_AUTHENTICATE_HTTP header may not match real DAV user.
    --| If DET call is successful, DAV_AUTHENTICATE_HTTP checks whether the user have read permission on mount point collection.
    --| Thus even if DET function allows anonymous access, the whole request may fail if mountpoint is not readable by public.
    create function "nntp_DAV_AUTHENTICATE_HTTP" (in id any, in what char(1), in req varchar, in can_write_http integer, inout a_lines any, inout a_uname varchar, inout a_pwd varchar, inout a_uid integer, inout a_gid integer, inout _perms varchar) returns integer
    {
      declare rc integer;
      declare puid, pgid, ruid, rgid integer;
      declare u_password, pperms varchar;
      -- anon are never allowed for mails! declare allow_anon integer;
      if (length (req) <> 3)
        return -15;
    
      whenever not found goto nf_col_or_res;
      if ((what <> 'R') and (what <> 'C'))
        return -14;
      -- allow_anon := WS.WS.PERM_COMP (substring (cast (pperms as varchar), 7, 3), req);
      if (a_uid is null)
        {
          -- if ((not allow_anon) or ('' <> WS.WS.FINDPARAM (a_lines, 'Authorization:')))
          rc := WS.WS.GET_DAV_AUTH (a_lines, 0, can_write_http, a_uname, u_password, a_uid, a_gid, _perms);
          if (rc < 0)
            return rc;
        }
      if (isinteger (a_uid))
        {
          if (a_uid < 0)
      return a_uid;
         if (a_uid = 1) -- Anonymous FTP
      {
              a_uid := http_nobody_uid ();
        a_gid := http_nogroup_gid ();
      }
        }
      if ((a_uid <> id[3]) and (a_uid <> http_dav_uid()))
        {
          -- dbg_obj_princ ('a_uid is ', a_uid, ', id[3] is ', id[3], ' mismatch');
          return -13;
        }
      if (not ('100' like req))
        return -13;
      return a_uid;
    
    nf_col_or_res:
      return -1;
    }
    ;
    
    
    --| This matches DAV_GET_PARENT (in id any, in st char(1), in path varchar) returns any
    create function "nntp_DAV_GET_PARENT" (in id any, in st char(1), in path varchar) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_GET_PARENT (', id, st, path, ')');
      return -20;
    }
    ;
    
    --| When DAV_COL_CREATE_INT calls DET function, authentication, check for lock and check for overwrite are passed, uid and gid are translated from strings to IDs.
    --| Check for overwrite, but the deletion of previously existing collection should be made by DET function.
    create function "nntp_DAV_COL_CREATE" (in detcol_id any, in path_parts any, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_COL_CREATE (', detcol_id, path_parts, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    --| It looks like that this is redundant and should be removed at all.
    create function "nntp_DAV_COL_MOUNT" (in detcol_id any, in path_parts any, in full_mount_path varchar, in mount_det varchar, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_COL_MOUNT (', detcol_id, path_parts, full_mount_path, mount_det, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    --| It looks like that this is redundant and should be removed at all.
    create function "nntp_DAV_COL_MOUNT_HERE" (in parent_id any, in full_mount_path varchar, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_COL_MOUNT_HERE (', parent_id, full_mount_path, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    
    --| When DAV_DELETE_INT calls DET function, authentication and check for lock are passed.
    create function "nntp_DAV_DELETE" (in detcol_id any, in path_parts any, in what char(1), in silent integer, in auth_uid integer) returns integer
    {
      -- dbg_obj_princ ('nntp_DAV_DELETE (', detcol_id, path_parts, what, silent, auth_uid, ')');
      return -20;
    }
    ;
    
    --| When DAV_RES_UPLOAD_STRSES_INT calls DET function, authentication and check for locks are performed before the call.
    --| There's a special problem, known as 'Transaction deadlock after reading from HTTP session'.
    --| The DET function should do only one INSERT of the 'content' into the table and do it as late as possible.
    --| The function should return -29 if deadlocked or otherwise broken after reading blob from HTTP.
    create function "nntp_DAV_RES_UPLOAD" (in detcol_id any, in path_parts any, inout content any, in type varchar, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_RES_UPLOAD (', detcol_id, path_parts, ', [content], ', content, type, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    
    --| When DAV_PROP_REMOVE_INT calls DET function, authentication and check for locks are performed before the call.
    --| The check whether it's a system name or not is _not_ permitted.
    create function "nntp_DAV_PROP_REMOVE" (in id any, in what char(0), in propname varchar, in silent integer, in auth_uid integer) returns integer
    {
      -- dbg_obj_princ ('nntp_DAV_PROP_REMOVE (', id, what, propname, silent, auth_uid, ')');
      return -20;
    }
    ;
    
    --| When DAV_PROP_SET_INT calls DET function, authentication and check for locks are performed before the call.
    --| The check whether it's a system property or not is _not_ permitted and the function should return -16 for live system properties.
    create function "nntp_DAV_PROP_SET" (in id any, in what char(0), in propname varchar, in propvalue any, in overwrite integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_PROP_SET (', id, what, propname, propvalue, overwrite, auth_uid, ')');
      if (propname[0] = 58)
        {
          return -16;
        }
      return -20;
    }
    ;
    
    --| When DAV_PROP_GET_INT calls DET function, authentication and check whether it's a system property are performed before the call.
    create function "nntp_DAV_PROP_GET" (in id any, in what char(0), in propname varchar, in auth_uid integer)
    {
      -- dbg_obj_princ ('nntp_DAV_PROP_GET (', id, what, propname, auth_uid, ')');
      return -11;
    }
    ;
    
    --| When DAV_PROP_LIST_INT calls DET function, authentication is performed before the call.
    --| The returned list should contain only user properties.
    create function "nntp_DAV_PROP_LIST" (in id any, in what char(0), in propmask varchar, in auth_uid integer)
    {
      -- dbg_obj_princ ('nntp_DAV_PROP_LIST (', id, what, propmask, auth_uid, ')');
      return vector ();
    }
    ;
    
    --| When DAV_PROP_GET_INT or DAV_DIR_LIST_INT calls DET function, authentication is performed before the call.
    create function "nntp_DAV_DIR_SINGLE" (in id any, in what char(0), in path any, in auth_uid integer) returns any
    {
      declare mnamefmt varchar;
      declare folder_id, server_id varchar;
      declare fullpath, rightcol, resname varchar;
      declare maxrcvdate datetime;
      declare colname varchar;
      --dbg_obj_princ ('nntp_DAV_DIR_SINGLE (', id, what, path, auth_uid, ')');
      server_id := cast(id[4] as varchar);
      folder_id := cast(id[5] as varchar);
      fullpath := '';
      rightcol := NULL;  
      if (atoi(folder_id) = 0)
      {
        maxrcvdate := (select FTHR_DATE from DB.DBA.NNFE_THR where FTHR_MESS_ID = folder_id);
        while (folder_id is not null)
        {
          colname := (select "nntp_COMPOSE_COMMENTS_NAME"(FTHR_SUBJ, FTHR_MESS_ID)
            from DB.DBA.NNFE_THR where FTHR_MESS_ID = folder_id);
          if (DAV_HIDE_ERROR (colname) is null)
            return -1;
          if (rightcol is null)
            rightcol := colname;
          fullpath := colname || '/' || fullpath;
          folder_id := coalesce ((select FTHR_REFER from DB.DBA.NNFE_THR where FTHR_MESS_ID = folder_id));
        }
        folder_id := cast((select FTHR_GROUP from DB.DBA.NNFE_THR where FTHR_MESS_ID = id[5]) as varchar);    
      }
      if ((server_id = '0' or server_id is null) and folder_id is not null)
      {
        server_id := folder_id;
        folder_id := null;
      }
      if (folder_id is not null)
      {
        if (maxrcvdate is null)
          maxrcvdate := (select NG_UP_TIME from DB.DBA.NEWS_GROUPS where NG_GROUP = folder_id);
        colname := (select "nntp_FIXNAME"(NG_NAME) from DB.DBA.NEWS_GROUPS where NG_GROUP = atoi(folder_id));
        if (DAV_HIDE_ERROR (colname) is null)
          return -1;
        if (rightcol is null)
          rightcol := colname;
        fullpath := colname || '/' || fullpath;
      }
      if (server_id is not null)
      {
        if (maxrcvdate is null)
          maxrcvdate := coalesce ((select max(NG_UP_TIME) from DB.DBA.NEWS_GROUPS where NG_SERVER = atoi(folder_id)),
          cast ('1980-01-01' as datetime) );
        colname := (select "nntp_FIXNAME" (concat(NS_SERVER, ':', cast(NS_PORT as varchar)))
          from DB.DBA.NEWS_SERVERS where NS_ID = atoi(server_id));
        if (DAV_HIDE_ERROR (colname) is null)
          return -1;
        if (rightcol is null)
          rightcol := colname;
        fullpath := colname || '/' || fullpath;
      }
      fullpath := DAV_CONCAT_PATH (DAV_SEARCH_PATH (id[1], 'C'), fullpath);
      if ('C' = what)
      {
        if (id[6] >= 0)
          return -1;
        return vector (fullpath, 'C', 0, maxrcvdate,
          id, '100000000NN', 0, id[3], maxrcvdate, 'dav/unix-directory', rightcol );
      }
      for select "nntp_COMPOSE_HTML_NAME"(FTHR_SUBJ, FTHR_MESS_ID) as orig_mname,
            FTHR_MESS_ID as m_id, FTHR_DATE
          from DB.DBA.NNFE_THR
          where FTHR_MESS_ID = id[6]
        do
        {
          return vector (fullpath || orig_mname, 'R', 1024, FTHR_DATE,
            id, 
            '100000000NN', 0, id[3], FTHR_DATE, 'text/plain', orig_mname);
        }
      return -1;
    }
    ;
    
    --| When DAV_PROP_GET_INT or DAV_DIR_LIST_INT calls DET function, authentication is performed before the call.
    create function "nntp_DAV_DIR_LIST" (in detcol_id any, in path_parts any, in detcol_path varchar, in name_mask varchar, in recursive integer, in auth_uid integer) returns any
    {
      --dbg_obj_princ ('nntp_DAV_DIR_LIST (', detcol_id, path_parts, detcol_path, name_mask, recursive, auth_uid, ')');
      declare mgroup_id, muser_id integer;
      declare mfolder_id, mserver_id varchar;
      declare top_davpath varchar;
      declare res, grand_res any;
      declare top_id, descnames any;
      declare reslen integer;
      declare what char (1);
      declare access varchar;
      declare ownergid, owner_uid, dn_ctr, dn_count integer;
      "nntp_ACCESS_PARAMS" (detcol_id, access, ownergid, owner_uid);
      if ((0 = length (path_parts)) or ('' = path_parts[length (path_parts) - 1]))
        what := 'C';
      else
        what := 'R';
      mgroup_id := 0;
      mserver_id := NULL;
      mfolder_id := NULL;
      grand_res := vector();
      if ('C' = what and 1 = length(path_parts))
        top_id := vector (UNAME'nntp', detcol_id, mgroup_id, owner_uid, null, null, -1); -- may be a fake id because top_id[4] may be NULL
      else
        top_id := "nntp_DAV_SEARCH_ID_IMPL" (detcol_id, path_parts, what, mgroup_id, muser_id, mserver_id, mfolder_id);
      if (DAV_HIDE_ERROR (top_id) is null)
        return vector();
      top_davpath := DAV_CONCAT_PATH (detcol_path, path_parts);
      if ('R' = what)
        return vector ("nntp_DAV_DIR_SINGLE" (top_id, what, top_davpath, auth_uid));
      res := vector();
      reslen := 0;
      if ('C' = what and 1 = length(path_parts))
      {
        for select "nntp_FIXNAME" (concat(NS_SERVER, ':', cast(NS_PORT as varchar))) as orig_name, cast(NS_ID as varchar) as f_id
          from DB.DBA.NEWS_SERVERS
          order by 1, 2
        do
        {
          declare maxrcvdate datetime;
          maxrcvdate := coalesce((select max(NG_UP_TIME)
            from DB.DBA.NEWS_GROUPS
            where
            NG_SERVER = atoi(f_id)),
            cast('1980-01-01' as datetime));
          res := vector_concat (res, vector (vector (DAV_CONCAT_PATH (top_davpath, orig_name) || '/', 'C', 0, maxrcvdate,
            vector (UNAME'nntp', detcol_id, mgroup_id, owner_uid, mserver_id, f_id, -1),
            '100000000NN', ownergid, owner_uid, maxrcvdate, 'dav/unix-directory', orig_name) ) );
        }
        grand_res := res;
      }
      if ('C' = what and length(path_parts) = 2)
      {
        for select "nntp_FIXNAME" (NG_NAME) as orig_name, NG_UP_TIME as maxrcvdate, cast(NG_GROUP as varchar) as f_id
          from DB.DBA.NEWS_GROUPS
          where NG_SERVER = top_id[5]
          order by 1, 2
        do
        { 
          res := vector_concat (res, vector (vector (DAV_CONCAT_PATH (top_davpath, orig_name) || '/', 'C', 0, maxrcvdate,
            vector (UNAME'nntp', detcol_id, mgroup_id, owner_uid, mserver_id, f_id, -1),
            '100000000NN', ownergid, owner_uid, maxrcvdate, 'dav/unix-directory', orig_name) ) );
        }
        grand_res := res;    
      }
      if ('C' = what and length(path_parts) > 2)
      {
        for select distinct FTHR_REFER as f_ref
          from DB.DBA.NNFE_THR
          where FTHR_GROUP = atoi(top_id[5]) and FTHR_REFER is not null
        do
        {
          for select "nntp_COMPOSE_COMMENTS_NAME"(FTHR_SUBJ, FTHR_MESS_ID) as orig_name, FTHR_MESS_ID as f_id, FTHR_DATE
            from DB.DBA.NNFE_THR
            where f_ref = FTHR_MESS_ID and FTHR_TOP = 1 and FTHR_SUBJ like name_mask
            order by 1, 2
          do
          {
            res := vector_concat(res, vector (vector (DAV_CONCAT_PATH (top_davpath, orig_name) || '/', 'C', 0, FTHR_DATE,
              vector (UNAME'nntp', detcol_id, mgroup_id, owner_uid,  mserver_id, f_id, -1),
              '100000000NN', ownergid, owner_uid, FTHR_DATE, 'dav/unix-directory', orig_name) ) );
            reslen := reslen + 1;
          }
        }
        for select distinct FTHR_MESS_ID as f_ref2
          from DB.DBA.NNFE_THR
          where FTHR_REFER = top_id[5]
        do
        {
          for select "nntp_COMPOSE_COMMENTS_NAME"(FTHR_SUBJ, FTHR_MESS_ID) as orig_name,
              FTHR_MESS_ID as f_id, FTHR_DATE
            from DB.DBA.NNFE_THR
            where f_ref2 = FTHR_MESS_ID
              and FTHR_SUBJ like name_mask
            order by 1, 2
          do
          {
            res := vector_concat(res, vector (vector (DAV_CONCAT_PATH (top_davpath, orig_name) || '/', 'C', 0, FTHR_DATE,
              vector (UNAME'nntp', detcol_id, mgroup_id, owner_uid,  mserver_id, f_id, -1),
              '100000000NN', ownergid, owner_uid, FTHR_DATE, 'dav/unix-directory', orig_name) ) );
            reslen := reslen + 1;
          }
        }
        grand_res := res;
        reslen := 0;
        res := vector();
        for select "nntp_COMPOSE_HTML_NAME"(FTHR_SUBJ, FTHR_MESS_ID) as orig_mname,
            FTHR_MESS_ID as m_id, FTHR_DATE
          from DB.DBA.NNFE_THR
          where ((FTHR_GROUP = atoi(top_id[5]) and FTHR_TOP=1) or FTHR_REFER = top_id[5])
            and FTHR_SUBJ like name_mask
          order by 1, 2
        do
        {
          res := vector_concat (res, vector (vector (DAV_CONCAT_PATH (top_davpath, orig_mname), 'R', 1024, FTHR_DATE,
            vector (UNAME'nntp', detcol_id, mgroup_id, owner_uid, mserver_id, top_id[5], m_id),
            '100000000NN', ownergid, owner_uid, FTHR_DATE, 'text/plain', orig_mname) ) );
          reslen := reslen + 1;
        }
        grand_res := vector_concat (grand_res, res);
      }
    finalize_res:
      return grand_res;
    }
    ;
                             
    --| When DAV_DIR_FILTER_INT calls DET function, authentication is performed before the call and compilation is initialized.
    create function "nntp_DAV_DIR_FILTER" (in detcol_id any, in path_parts any, in detcol_path any, inout compilation any, in recursive integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_DIR_FILTER (', detcol_id, path_parts, detcol_path, compilation, recursive, auth_uid, ')');
      return vector();
    }
    ;
    
    create function "nntp_DAV_SEARCH_ID_IMPL" (in detcol_id any, in path_parts any, in what char(1), inout group_id integer, inout muser_id integer, inout mserver_id varchar, inout mfolder_id varchar) returns any
    {
      declare colpath, merged_fnameext, orig_fnameext varchar;
      declare orig_id varchar;
      declare ctr, len integer;
      declare hitlist any;
      declare access varchar;
      declare ownergid, owner_uid integer;
      --dbg_obj_princ ('nntp_DAV_SEARCH_ID_IMPL (', detcol_id, path_parts, what, group_id, muser_id, mfolder_id, ')');
      "nntp_ACCESS_PARAMS" (detcol_id, access, ownergid, owner_uid);
      if (0 = length(path_parts))
      {
        if ('C' <> what)
        {
          return -1;
        }
        return vector (UNAME'nntp', detcol_id, group_id, owner_uid, mserver_id, mfolder_id, -1);
      }
      if ('' = path_parts[length(path_parts) - 1])
      {
        if ('C' <> what)
        {
          return -1;
        }
      }
      else
      {
        if ('R' <> what)
        {
          return -1;
        }
      }
      len := length (path_parts) - 1;
      ctr := 0;
      while (ctr < len)
      {
        if (ctr = 0)
        {
          hitlist := vector ();
          for select NS_ID
            from DB.DBA.NEWS_SERVERS
            where "nntp_FIXNAME"(concat(NS_SERVER, ':', cast(NS_PORT as varchar))) = path_parts[ctr]
          do 
          {
            hitlist := vector_concat (hitlist, vector (NS_ID));
          }
          if (length (hitlist) <> 1)
            return -1;
          mfolder_id := cast(hitlist[0] as varchar);
          if (len > 1)
            mserver_id := cast(hitlist[0] as varchar);
        }
        if (ctr = 1)
        {
          hitlist := vector ();
          for select NG_GROUP
            from DB.DBA.NEWS_GROUPS
            where "nntp_FIXNAME"(NG_NAME) = path_parts[ctr]
          do 
          {
            hitlist := vector_concat (hitlist, vector (NG_GROUP));
          }
          if (length (hitlist) <> 1)
            return -1;
          mfolder_id := cast(hitlist[0] as varchar);
        }
        if (ctr > 1)
        {
          hitlist := vector ();
          for select FTHR_MESS_ID from DB.DBA.NNFE_THR
            where "nntp_COMPOSE_COMMENTS_NAME"(FTHR_SUBJ, FTHR_MESS_ID) = path_parts[ctr]
          do 
          {
            hitlist := vector_concat (hitlist, vector (FTHR_MESS_ID));
          }
          if (length (hitlist) <> 1)
            return -1;
          mfolder_id := cast(hitlist[0] as varchar);
        }
        ctr := ctr + 1;
      }
      if ('C' = what)
        return vector (UNAME'nntp', detcol_id, group_id, owner_uid, mserver_id, mfolder_id, -1);
    
      len := length (path_parts);
      while (ctr < len)
      {
        hitlist := vector ();
        for select FTHR_MESS_ID
          from DB.DBA.NNFE_THR
          where (FTHR_GROUP = atoi(mfolder_id) or (FTHR_REFER = mfolder_id)) and
          ("nntp_COMPOSE_HTML_NAME" (FTHR_SUBJ, FTHR_MESS_ID) = path_parts[ctr])
        do 
        {
          hitlist := vector_concat (hitlist, vector (FTHR_MESS_ID));
        }
        if (length (hitlist) <> 1)
          return -1;
        ctr := ctr + 1;
      }  
      return vector (UNAME'nntp', detcol_id, group_id, owner_uid, mserver_id, mfolder_id, hitlist[0]);
    }
    ;
    
    --| When DAV_PROP_GET_INT or DAV_DIR_LIST_INT calls DET function, authentication is performed before the call.
    create function "nntp_DAV_SEARCH_ID" (in detcol_id any, in path_parts any, in what char(1)) returns any
    {
      declare mgroup_id,  muser_id integer;
      declare mfolder_id, mserver_id varchar;
      --dbg_obj_princ ('nntp_DAV_SEARCH_ID (', detcol_id, path_parts, what, ')');
      return "nntp_DAV_SEARCH_ID_IMPL" (detcol_id, path_parts, what, mgroup_id, muser_id, mserver_id, mfolder_id);
    }
    ;
    
    --| When DAV_SEARCH_PATH_INT calls DET function, authentication is performed before the call.
    create function "nntp_DAV_SEARCH_PATH" (in id any, in what char(1)) returns any
    {
      --dbg_obj_princ ('nntp_DAV_SEARCH_PATH (', id, what, ')');
      return NULL;
    }
    ;
    
    --| When DAV_COPY_INT calls DET function, authentication and check for locks are performed before the call, but no check for existing/overwrite.
    create function "nntp_DAV_RES_UPLOAD_COPY" (in detcol_id any, in path_parts any, in source_id any, in what char(1), in overwrite_flags integer, in permissions varchar, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_RES_UPLOAD_COPY (', detcol_id, path_parts, source_id, what, overwrite_flags, permissions, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    --| When DAV_COPY_INT calls DET function, authentication and check for locks are performed before the call, but no check for existing/overwrite.
    create function "nntp_DAV_RES_UPLOAD_MOVE" (in detcol_id any, in path_parts any, in source_id any, in what char(1), in overwrite_flags integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_RES_UPLOAD_MOVE (', detcol_id, path_parts, source_id, what, overwrite_flags, auth_uid, ')');
      return -20;
    }
    ;
    
    --| When DAV_RES_CONTENT or DAV_RES_COPY_INT or DAV_RES_MOVE_INT calls DET function, authentication is made.
    --| If content_mode is 1 then content is a valid output stream before the call.
    create function "nntp_DAV_RES_CONTENT" (in id any, inout content any, out type varchar, in content_mode integer) returns integer
    {
      --dbg_obj_princ ('nntp_DAV_RES_CONTENT (', id, ', [content], [type], ', content_mode, ')');
      declare str_out any;
      str_out := string_output();
      content := '';
      if (id[6] is not null)
      {
        type := 'text/plain';
        
       declare _date, _from, _subj, _grps, _print_body, d_name varchar;
       declare _body, _head, parsed_message any;
       declare idx integer;
    
       set isolation='committed';
    
       select NM_HEAD, blob_to_string (NM_BODY)
         into parsed_message, _body
         from DB.DBA.NEWS_MSG
         where NM_ID = id[6];
    
       for (select NM_GROUP
              from DB.DBA.NEWS_MULTI_MSG
              where NM_KEY_ID = id[6]) do
         {
            if (ns_rest_rate_read (NM_GROUP) > 0)
              {
                 content := '<h3>Excessive read detected, please try again later.</h3>';
                 return 0;
              }
         }
    
       if (__tag (parsed_message) <> 193)
         parsed_message := mime_tree (_body);
    
       _head := parsed_message[0];
       _subj := coalesce (get_keyword_ucase ('Subject', _head), '');
       _from := coalesce (get_keyword_ucase ('From', _head), '');
       _grps := coalesce (get_keyword_ucase ('Newsgroups', _head), '');
       _date := coalesce (get_keyword_ucase ('Date', _head), '');
    
       nntpf_decode_subj (_subj);
    
       nntpf_decode_subj (_from);
       _from := nntpf_replace_at (_from);
    
       http ('<div class="artheaders">', str_out);
       http (sprintf ('<span class="header">From:</span>%V<br/>', _from), str_out);
       http (sprintf ('<span class="header">Subject:</span>%s<br/>', _subj), str_out);
       http (sprintf ('<span class="header">Newsgroups:</span>%s<br/>', _grps), str_out);
       http (sprintf ('<span class="header">Date:</span>%s<br/>', _date), str_out);
       http ('</div><br/>', str_out);
    
    
       --if (parsed_message[2] <> 0)
       --  return nntpf_display_article_multi_part (parsed_message, _body, id, sid);
    
       --nntpf_display_message_reply (sid, id);
    
        _print_body := subseq (_body, parsed_message[1][0], parsed_message[1][1]);
        if (length (_print_body) > 3)
           _print_body := subseq (_print_body, 0, (length (_print_body) - 3));
    
       -- CLEAR THIS
    
       parsed_message := nntpf_get_mess_attachments (_print_body, 0);
    
       _print_body := parsed_message[0];
       
       "nntpf_display_message_text2" (_print_body, get_keyword_ucase ('Content-Type', _head), str_out);
    
       http ('<br/>', str_out);
       idx := 1;
       while (idx < length (parsed_message))
         {
            d_name := parsed_message[idx];
            http (sprintf ('Download attachment : <a href="http://%s/INLINEFILE/%s?VSP=/nntpf/attachment.vsp&id=%U&part=%i&fn=%s"> %s </a><br/>',
                           nntpf_get_host (vector ()),
                           d_name,
                           encode_base64 (id),
                           idx,
                           d_name,
                           d_name), str_out);
    
            if (d_name like '%.jpg' or d_name like '%.gif')
              {
                 http (sprintf ('<img alt="attachment" src="http://%s/INLINEFILE/%s?VSP=/nntpf/attachment.vsp&id=%U&part=%i&fn=%s">',
                       nntpf_get_host (vector()),
                       d_name,
                       encode_base64 (id),
                       idx,
                       d_name), str_out);
                 http ('<br/><br/><br/>', str_out);
              }
    
            idx := idx + 1;
         }
      }
      content := string_output_string(str_out);
      return 0;
    }
    ;
    
    --| This adds an extra access path to the existing resource or collection.
    create function "nntp_DAV_SYMLINK" (in detcol_id any, in path_parts any, in source_id any, in what char(1), in overwrite integer, in uid integer, in gid integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_SYMLINK (', detcol_id, path_parts, source_id, overwrite, uid, gid, auth_uid, ')');
      return -20;
    }
    ;
    
    --| This gets a list of resources and/or collections as it is returned by DAV_DIR_LIST and and writes the list of quads (old_id, 'what', old_full_path, dereferenced_id, dereferenced_full_path).
    create function "nntp_DAV_DEREFERENCE_LIST" (in detcol_id any, inout report_array any) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_DEREFERENCE_LIST (', detcol_id, report_array, ')');
      return -20;
    }
    ;
    
    --| This gets one of reference quads returned by ..._DAV_REREFERENCE_LIST() and returns a record (new_full_path, new_dereferenced_full_path, name_may_vary).
    create function "nntp_DAV_RESOLVE_PATH" (in detcol_id any, inout reference_item any, inout old_base varchar, inout new_base varchar) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_RESOLVE_PATH (', detcol_id, reference_item, old_base, new_base, ')');
      return -20;
    }
    ;
    
    --| There's no API function to lock for a while (do we need such?) The "LOCK" DAV method checks that all parameters are valid but does not check for existing locks.
    create function "nntp_DAV_LOCK" (in path any, in id any, in type char(1), inout locktype varchar, inout scope varchar, in token varchar, inout owner_name varchar, inout owned_tokens varchar, in depth varchar, in timeout_sec integer, in auth_uid integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_LOCK (', id, type, locktype, scope, token, owner_name, owned_tokens, depth, timeout_sec, owner_name, auth_uid, ')');
      return -20;
    }
    ;
    
    
    --| There's no API function to unlock for a while (do we need such?) The "UNLOCK" DAV method checks that all parameters are valid but does not check for existing locks.
    create function "nntp_DAV_UNLOCK" (in id any, in type char(1), in token varchar, in auth_uid integer)
    {
      -- dbg_obj_princ ('nntp_DAV_UNLOCK (', id, type, token, auth_uid, ')');
      return -27;
    }
    ;
    
    --| The caller does not check if id is valid.
    --| This returns -1 if id is not valid, 0 if all existing locks are listed in owned_tokens whitespace-delimited list, 1 for soft 2 for hard lock.
    create function "nntp_DAV_IS_LOCKED" (inout id any, inout type char(1), in owned_tokens varchar) returns integer
    {
      -- dbg_obj_princ ('nntp_DAV_IS_LOCKED (', id, type, owned_tokens, ')');
      return 0;
    }
    ;
    
    
    --| The caller does not check if id is valid.
    --| This returns -1 if id is not valid, list of tuples (LOCK_TYPE, LOCK_SCOPE, LOCK_TOKEN, LOCK_TIMEOUT, LOCK_OWNER, LOCK_OWNER_INFO) otherwise.
    create function "nntp_DAV_LIST_LOCKS" (in id any, in type char(1), in recursive integer) returns any
    {
      -- dbg_obj_princ ('nntp_DAV_LIST_LOCKS" (', id, type, recursive);
      return vector ();
    }
    ;