Retrieving Directory Contents from Nextcloud Using Oracle PL/SQL

Retrieving Directory Contents from Nextcloud Using Oracle PL/SQL

The get_directory_contents function is our powerful Oracle PL/SQL solution for querying directory contents from a Nextcloud server. By using the UTL_HTTP package to interact with Nextcloud's WebDAV interface, the function can list files and directories, providing metadata such as filenames, modification dates, file sizes, and MIME types.

Below is a detailed walkthrough of the function, along with the full code.


Full Code of get_directory_contents Function

FUNCTION get_directory_contents(p_dir VARCHAR2) RETURN DIR_TABLE
AS
    v_wallet_path VARCHAR2(400);
    v_wallet_password VARCHAR2(200);
    v_url           VARCHAR2(1000);
    v_username      VARCHAR2(1000);
    v_password      VARCHAR2(1000);
    v_http_request  UTL_HTTP.req;
    v_http_response UTL_HTTP.resp;
    v_response_text CLOB;  
    dirList         DIR_TABLE := DIR_TABLE();
    entrytype       VARCHAR2(5);
    v_name          VARCHAR2(100);
BEGIN 
    p_log.log_debug('retrieve nextcloud dir contents: ' || p_dir);
    UTL_HTTP.SET_COOKIE_SUPPORT(TRUE);
    v_wallet_path := p_adm.get_param('wallet_path','');
    v_wallet_password := p_adm.get_param_base64('wallet_password','');
    UTL_HTTP.SET_WALLET(v_wallet_path, v_wallet_password);
    v_username := p_adm.get_param('nextcloud_username', '');
    v_password := p_adm.get_param_base64('nextcloud_password', '');

    v_url := p_adm.get_param('nextcloud_basic_url', '') || UPPER(v_username);
    p_log.log_debug('url=' || v_url);
    if p_dir is not null then 
        v_url := v_url || '/' || p_dir ;
    end if;
    p_log.log_debug('url with dir=' || v_url);
    p_log.log_debug('before making utl_http PROPFIND request');
    v_http_request := UTL_HTTP.begin_request(v_url, 'PROPFIND', 'HTTP/1.1');
    UTL_HTTP.set_authentication(v_http_request, v_username, v_password, 'Basic');
    UTL_HTTP.set_header(v_http_request, 'OCS-APIREQUEST', 'true');
    UTL_HTTP.set_header(v_http_request, 'Depth', '1');
    v_http_response := UTL_HTTP.get_response(v_http_request);

    DBMS_LOB.createtemporary(v_response_text, TRUE);

    BEGIN
        LOOP
            DECLARE
                v_buffer VARCHAR2(32767);
            BEGIN
                UTL_HTTP.read_text(v_http_response, v_buffer, 32767);
                DBMS_LOB.writeappend(v_response_text, LENGTH(v_buffer), v_buffer);
            EXCEPTION
                WHEN UTL_HTTP.end_of_body THEN
                    EXIT;
            END;
        END LOOP;
    END;
    UTL_HTTP.end_response(v_http_response);

    p_log.log_debug('nextcloud xml response: ' || DBMS_LOB.substr(v_response_text, 10000, 1));

    for rec in (select  xt.href,xt.lastmodified,xt.contentlength,contenttype
        from XMLTABLE (
            xmlnamespaces('DAV:' as "d"),
            '/d:multistatus/d:response'
            PASSING XMLTYPE(v_response_text)
            COLUMNS
                href          VARCHAR2(1000)       PATH 'd:href',
                lastmodified  VARCHAR2(200)        PATH 'd:propstat/d:prop/d:getlastmodified',
                contentlength NUMBER               PATH 'd:propstat/d:prop/d:getcontentlength',
                contenttype   VARCHAR2(300)        PATH 'd:propstat/d:prop/d:getcontenttype'
            ) xt)
    loop
        dirList.extend;
        if (rec.contentlength is null) then entrytype := 'dir'; else entrytype := 'file'; end if; 
        if (entrytype = 'file') then 
            v_name := UTL_URL.unescape(SUBSTR(rec.href, INSTR(rec.href, '/', -1) + 1));
        else 
            v_name := get_lastpart_dir(rec.href);
        end if;
        dirList(dirList.COUNT) := dir_record(v_name, rec.contentlength, nextcloud_date(rec.lastmodified), rec.contenttype,entrytype);
    end loop;

    RETURN dirList;
END get_directory_contents;

Detailed Breakdown

1. Authentication Setup

The function initializes secure parameters like wallet path, username, and password:

v_wallet_path := p_adm.get_param('wallet_path','');
v_wallet_password := p_adm.get_param_base64('wallet_password','');
UTL_HTTP.SET_WALLET(v_wallet_path, v_wallet_password);
v_username := p_adm.get_param('nextcloud_username', '');
v_password := p_adm.get_param_base64('nextcloud_password', '');

2. URL Construction

The Nextcloud URL is dynamically built based on the provided directory (p_dir):

v_url := p_adm.get_param('nextcloud_basic_url', '') || UPPER(v_username);
if p_dir is not null then 
    v_url := v_url || '/' || p_dir ;
end if;

3. Sending HTTP Request

The PROPFIND method is configured, and authentication headers are added:

v_http_request := UTL_HTTP.begin_request(v_url, 'PROPFIND', 'HTTP/1.1');
UTL_HTTP.set_authentication(v_http_request, v_username, v_password, 'Basic');
UTL_HTTP.set_header(v_http_request, 'OCS-APIREQUEST', 'true');
UTL_HTTP.set_header(v_http_request, 'Depth', '1');

4. Reading the Response

The XML response is read and stored in a CLOB:

DBMS_LOB.createtemporary(v_response_text, TRUE);
BEGIN
    LOOP
        DECLARE
            v_buffer VARCHAR2(32767);
        BEGIN
            UTL_HTTP.read_text(v_http_response, v_buffer, 32767);
            DBMS_LOB.writeappend(v_response_text, LENGTH(v_buffer), v_buffer);
        EXCEPTION
            WHEN UTL_HTTP.end_of_body THEN
                EXIT;
        END;
    END LOOP;
END;
UTL_HTTP.end_response(v_http_response);

5. Parsing XML

XMLTABLE extracts metadata such as href, lastmodified, and contentlength:

for rec in (select  xt.href,xt.lastmodified,xt.contentlength,contenttype
    from XMLTABLE (
        xmlnamespaces('DAV:' as "d"),
        '/d:multistatus/d:response'
        PASSING XMLTYPE(v_response_text)
        COLUMNS
            href          VARCHAR2(1000)       PATH 'd:href',
            lastmodified  VARCHAR2(200)        PATH 'd:propstat/d:prop/d:getlastmodified',
            contentlength NUMBER               PATH 'd:propstat/d:prop/d:getcontentlength',
            contenttype   VARCHAR2(300)        PATH 'd:propstat/d:prop/d:getcontenttype'
    ) xt)
loop

6. Building the Directory List

Each record is stored in the DIR_TABLE with its type (file or directory):

if (rec.contentlength is null) then entrytype := 'dir'; else entrytype := 'file'; end if; 
if (entrytype = 'file') then 
    v_name := UTL_URL.unescape(SUBSTR(rec.href, INSTR(rec.href, '/', -1) + 1));
else 
    v_name := get_lastpart_dir(rec.href);
end if;
dirList(dirList.COUNT) := dir_record(v_name, rec.contentlength, nextcloud_date(rec.lastmodified), rec.contenttype,entrytype);

How to Use

This function allows Oracle developers to seamlessly retrieve directory contents from Nextcloud, making it easier to integrate cloud storage with enterprise databases.

The function can be further customized for specific needs, such as deeper directory traversal, additional metadata retrieval, or extended error handling.