create or replace package OFS is procedure init_root; function os_user return varchar2; -- Cursor type to return directory entries. -- Includes all OFS_ITEMS columns, except id, parent_id, and data. -- It also includes dbms_lob.getlength(data) as size. type dir_cursor is ref cursor; -- Get directory entries. -- %param p_path \dir1\dir2 -- %param p_mask hello*.da?[; mask2[; ...]] -- %param p_alldirs 'Y' - include all subdirs, 'N' - subdirs according mask -- %param p_crs cursor with the matching directory entries procedure get_dir(p_path in varchar2, p_mask in varchar2, p_alldirs in varchar2, p_crs in out dir_cursor); -- Read file data. -- %param p_filename \dir1\dir2\file.ext function read_file(p_filename in varchar2) return blob; -- Write file data. -- File must be unlocked or locked by current OS user and not read_only. -- For new files, parent directory must not be read_only. New files in a -- compressed directory are implicitly compressed. -- %param p_filename \dir1\dir2\file.ext -- %param p_data temporary BLOB with all file data procedure write_file(p_filename in varchar2, p_data in blob); -- Create directory. -- Parent directory must not be read_only. -- %param p_dirname \dir1\dir procedure create_dir(p_dirname in varchar2); -- Delete file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- Directory must not be read_only. -- Parent directory must not be read_only. -- %param p_dirname \dir1\dir or \dir1\file.ext procedure delete(p_itemname in varchar2); -- Set file or directory attribute. -- File must be unlocked or locked by current OS user. -- Compression is ignored on Oracle9.2 or earlier (utl_compress dependency). -- %param p_itemname The name of the file or directory -- %param p_attrname 'READ_ONLY', 'COMPRESSED' or 'LOCKED' -- %param p_value 'Y' or 'N' procedure set_attr(p_itemname in varchar2, p_attrname in varchar2, p_value in varchar2); -- Get file or directory attribute. -- %param p_itemname The name of the file or directory -- %param p_attrname 'READ_ONLY', 'COMPRESSED' or 'LOCKED' -- %return 'Y' or 'N' function get_attr(p_itemname in varchar2, p_attrname in varchar2) return varchar2; -- Get file or directory last modification date. function get_date(p_itemname in varchar2) return date; -- Lock file. -- File must be unlocked and not read_only. -- %param p_filename \dir1\dir2\file.ext procedure lock_file(p_filename in varchar2); -- Unlock file. -- File must be locked by current OS user -- %param p_filename \dir1\dir2\file.ext procedure unlock_file(p_filename in varchar2); -- Rename file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- %param p_itemname \dir1\dir2\file.ext -- %param p_newname newfile.ext procedure rename(p_itemname in varchar2, p_newname in varchar2); -- Move file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- %param p_itemname \dir1\dir2\file.ext -- %param p_newdir \dir1\dir2 procedure move(p_itemname in varchar2, p_newdir in varchar2); -- Copy file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- %param p_itemname \dir1\dir2\file.ext -- %param p_newdir \dir1\dir2 procedure copy(p_itemname in varchar2, p_newdir in varchar2); -- Tests if a specified file exists. -- %param p_filename \dir1\dir2\file.ext -- %return 'Y' or 'N' function file_exists(p_filename in varchar2) return char; -- Tests if a specified directory exists. -- %param p_path \dir1\dir2 -- %return 'Y' or 'N' function directory_exists(p_path in varchar2) return char; end OFS; / create or replace package body OFS is type VC2_array is table of OFS_ITEMS.name%type index by pls_integer; -- Set of routines to raise exceptions and check conditions. procedure select_error(p_msg varchar2, p_path in varchar2) is begin raise_application_error(-20000, p_msg || '. path=' || p_path); end; procedure unknown_attr_error(p_attr varchar2) is begin raise_application_error(-20000, 'OFS-9: Unknown attribute.' || '. attr=' || p_attr); end; procedure path_not_found_error(p_path in varchar2) is begin select_error('OFS-7: Path not found', p_path); end; procedure check_is_file(p_type in OFS_ITEMS.item_type%type, p_filename in varchar2) is begin if p_type <> 'F' then select_error('OFS-8: OFS item must be file', p_filename); end if; end; procedure check_is_directory(p_type in OFS_ITEMS.item_type%type, p_filename in varchar2) is begin if p_type <> 'D' then select_error('OFS-10: OFS item must be directory', p_filename); end if; end; procedure already_locked_error(p_filename in varchar2) is begin select_error('OFS-13: File is already locked', p_filename); end; procedure already_unlocked_error(p_filename in varchar2) is begin select_error('OFS-14: File is already unlocked', p_filename); end; function unquote_name(p_name in varchar2) return varchar2 is begin if SubStr(p_name, 1, 1) = '"' and SubStr(p_name, Length(p_name), 1) = '"' then return SubStr(p_name, 2, Length(p_name) - 2); else return p_name; end if; end; -- Parses path -- p_path The path to parse. -- p_raise_exc If True, then raise exception if path is not found. -- If False, then function return False if path not found. -- p_last_item Returns p_path last OFS item info. -- p_no_items Returns collection of p_path not found names. -- Returns True, if p_path found, else False. function path_to_item(p_path in varchar2, p_raise_exc in boolean, p_last_item out OFS_ITEMS%rowtype, p_no_items out VC2_array) return boolean is path varchar2(1000); item_name OFS_ITEMS.name%type; i pls_integer; prev_i pls_integer; j pls_integer; path_exists boolean; begin path := unquote_name(p_path); if path is null or path = '\' then path := '\*'; else if SubStr(path, 1, 1) <> '\' then path := '\' || path; end if; if SubStr(path, Length(path), 1) = '\' then path := SubStr(path, 1, Length(path) - 1); end if; end if; prev_i := 1; path_exists := true; j := 1; p_last_item.id := 0; loop i := Instr(path, '\', prev_i + 1); if i = 0 then item_name := unquote_name(SubStr(path, prev_i + 1, Length(path))); else item_name := unquote_name(SubStr(path, prev_i + 1, i - (prev_i + 1))); end if; if path_exists then begin select * into p_last_item from OFS_ITEMS where parent_id = p_last_item.id and Upper(name) = Upper(item_name); exception when no_data_found then if p_raise_exc then path_not_found_error(p_path); end if; path_exists := false; end; end if; if not path_exists then p_no_items(j) := item_name; j := j + 1; end if; exit when i = 0; prev_i := i; end loop; return path_exists; end; -- Convert DOS file mask into Oracle LIKE mask function mask_to_like(p_mask in varchar2) return varchar2 is ch char; res varchar2(300); begin if (p_mask is null) or (p_mask = '*.*') then return '%'; else res := ''; for i in 1 .. Length(p_mask) loop ch := SubStr(p_mask, i, 1); if ch = '?' then res := res || '_'; elsif ch = '*' then res := res || '%'; elsif (ch = '_') or (ch = '%') or (ch = '\') then res := res || '\' || ch; else res := res || ch; end if; end loop; return res; end if; end; -- Get directory entries. -- %param p_path \dir1\dir2 -- %param p_mask hello*.da?[; mask2[; ...]] -- %param p_alldirs 'Y' - include all subdirs, 'N' - subdirs according mask -- %param p_crs cursor with the matching directory entries procedure get_dir(p_path in varchar2, p_mask in varchar2, p_alldirs in varchar2, p_crs in out dir_cursor) is item OFS_ITEMS%rowtype; no_items VC2_array; ora_mask varchar2(300); dir_q varchar2(1000); i pls_integer; prev_i pls_integer; first_mask boolean; begin if not path_to_item(p_path, true, item, no_items) then return; end if; check_is_directory(item.item_type, p_path); dir_q := 'select ITEM_TYPE, NAME, DATE_CREATED, CREATED_BY,' || ' DATE_MODIFIED, MODIFIED_BY, READ_ONLY, COMPRESSED,' || ' dbms_lob.getlength(DATA) as DATA_LENGTH, LOCKED_BY, DATE_LOCKED ' || 'from OFS_ITEMS i ' || 'where PARENT_ID = :PID and PARENT_ID <> ID'; prev_i := 0; first_mask := true; loop i := Instr(p_mask, ';', prev_i + 1); if i = 0 then ora_mask := SubStr(p_mask, prev_i + 1, Length(p_mask)); else ora_mask := SubStr(p_mask, prev_i + 1, i - (prev_i + 1)); end if; if Length(ora_mask) <> 0 then ora_mask := Upper(mask_to_like(ora_mask)); if first_mask then dir_q := dir_q || ' and ('; first_mask := false; else dir_q := dir_q || ' or '; end if; dir_q := dir_q || 'Upper(NAME) like ''' || ora_mask || ''' escape ''\'''; end if; exit when i = 0; prev_i := i; end loop; if not first_mask and p_alldirs = 'Y' then dir_q := dir_q || ' or ITEM_TYPE = ''D'''; end if; if not first_mask then dir_q := dir_q || ')'; end if; dir_q := dir_q || ' order by ITEM_TYPE, NAME'; open p_crs for dir_q using item.id; end; -- Read file data. -- %param p_filename \dir1\dir2\file.ext function read_file(p_filename in varchar2) return blob is item OFS_ITEMS%rowtype; no_items VC2_array; begin if path_to_item(p_filename, true, item, no_items) then check_is_file(item.item_type, p_filename); if ofs_compress.is_active and item.compressed = 'Y' then return ofs_compress.uncomp(item.data); else return item.data; end if; else return null; end if; end; -- Write file data. -- File must be unlocked or locked by current OS user and not read_only. -- For new files, parent directory must not be read_only. New files in a -- compressed directory are implicitly compressed. -- %param p_filename \dir1\dir2\file.ext -- %param p_data temporary BLOB with all file data procedure write_file(p_filename in varchar2, p_data in blob) is item OFS_ITEMS%rowtype; no_items VC2_array; begin if path_to_item(p_filename, false, item, no_items) then check_is_file(item.item_type, p_filename); update OFS_ITEMS set modified_by = OFS_private.os_user where id = item.id; else if no_items.count > 1 then path_not_found_error(p_filename); end if; insert into OFS_ITEMS (parent_id, item_type, name, data) values (item.id, 'F', no_items(1), empty_blob()) returning data into item.data; end if; if ofs_compress.is_active and item.compressed = 'Y' then ofs_compress.comp(p_data, item.data); else dbms_lob.trim(item.data, 0); dbms_lob.append(item.data, p_data); end if; end; -- Create directory. -- Parent directory must not be read_only. -- %param p_dirname \dir1\dir procedure create_dir(p_dirname in varchar2) is item OFS_ITEMS%rowtype; no_items VC2_array; begin if path_to_item(p_dirname, false, item, no_items) then check_is_directory(item.item_type, p_dirname); select_error('OFS-11: Specified directory already exists', p_dirname); else for i in 1 .. no_items.count loop insert into OFS_ITEMS (parent_id, item_type, name) values (item.id, 'D', no_items(i)) returning id into item.id; end loop; end if; end; -- Delete file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- Directory must not be read_only. -- Parent directory must not be read_only. -- %param p_dirname \dir1\dir or \dir1\file.ext procedure delete(p_itemname in varchar2) is item OFS_ITEMS%rowtype; no_items VC2_array; begin if path_to_item(p_itemname, true, item, no_items) then delete from OFS_ITEMS where id = item.id; end if; end; -- Set file or directory attribute. -- File must be unlocked or locked by current OS user. -- Compression is ignored on Oracle9.2 or earlier (utl_compress dependency). -- %param p_itemname The name of the file or directory -- %param p_attrname 'READ_ONLY', 'COMPRESSED' or 'LOCKED' -- %param p_value 'Y' or 'N' procedure set_attr(p_itemname in varchar2, p_attrname in varchar2, p_value in varchar2) is item OFS_ITEMS%rowtype; no_items VC2_array; attrname varchar2(20); val char(1); temp_lob blob; begin if path_to_item(p_itemname, true, item, no_items) then attrname := Upper(p_attrname); val := Upper(p_value); if attrname = 'READ_ONLY' then if item.read_only <> val then update OFS_ITEMS set read_only = val where id = item.id; end if; elsif attrname = 'COMPRESSED' then if item.compressed <> val then update OFS_ITEMS set compressed = val where id = item.id; if ofs_compress.is_active then dbms_lob.createtemporary(temp_lob, false); begin dbms_lob.append(temp_lob, item.data); if val = 'Y' then ofs_compress.comp(temp_lob, item.data); else ofs_compress.uncomp(temp_lob, item.data); end if; exception when others then dbms_lob.freetemporary(temp_lob); raise; end; dbms_lob.freetemporary(temp_lob); end if; end if; elsif attrname = 'LOCKED' then check_is_file(item.item_type, p_itemname); if val = 'Y' then if Upper(item.locked_by) = Upper(OFS_private.os_user) then already_locked_error(p_itemname); end if; update OFS_ITEMS set locked_by = OFS_private.os_user where id = item.id; elsif val = 'N' then if item.locked_by is null then already_unlocked_error(p_itemname); end if; update OFS_ITEMS set locked_by = null where id = item.id; end if; else unknown_attr_error(p_attrname); end if; end if; end; -- Get file or directory attribute. -- %param p_itemname The name of the file or directory -- %param p_attrname 'READ_ONLY', 'COMPRESSED' or 'LOCKED' -- %return 'Y' or 'N' function get_attr(p_itemname in varchar2, p_attrname in varchar2) return varchar2 is item OFS_ITEMS%rowtype; no_items VC2_array; attrname varchar2(20); begin if path_to_item(p_itemname, true, item, no_items) then attrname := Upper(p_attrname); if attrname = 'READ_ONLY' then return item.read_only; elsif attrname = 'COMPRESSED' then return item.compressed; elsif attrname = 'LOCKED' then if item.locked_by is not null then return 'Y'; else return 'N'; end if; elsif attrname = 'ID' then return to_char(item.id); else unknown_attr_error(p_attrname); end if; else return null; end if; end; -- Get file or directory last modification date. function get_date(p_itemname in varchar2) return date is item OFS_ITEMS%rowtype; no_items VC2_array; begin if path_to_item(p_itemname, true, item, no_items) then return item.date_modified; else return null; end if; end; -- Lock file. -- File must be unlocked and not read_only. -- %param p_filename \dir1\dir2\file.ext procedure lock_file(p_filename in varchar2) is begin set_attr(p_filename, 'LOCKED', 'Y'); end; -- Unlock file. -- File must be locked by current OS user -- %param p_filename \dir1\dir2\file.ext procedure unlock_file(p_filename in varchar2) is begin set_attr(p_filename, 'LOCKED', 'N'); end; -- Rename file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- %param p_itemname \dir1\dir2\file.ext -- %param p_newname newfile.ext procedure rename(p_itemname in varchar2, p_newname in varchar2) is item OFS_ITEMS%rowtype; no_items VC2_array; item_name OFS_ITEMS.NAME%type; begin item_name := unquote_name(p_newname); if Instr(item_name, '\') <> 0 then select_error('OFS-12: New name must be without path', p_newname); end if; if path_to_item(p_itemname, true, item, no_items) then update OFS_ITEMS set name = item_name where id = item.id; end if; end; -- Move file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- %param p_itemname \dir1\dir2\file.ext -- %param p_newdir \dir1\dir2 procedure move(p_itemname in varchar2, p_newdir in varchar2) is item1 OFS_ITEMS%rowtype; no_items1 VC2_array; item2 OFS_ITEMS%rowtype; no_items2 VC2_array; begin if path_to_item(p_itemname, true, item1, no_items1) and path_to_item(p_newdir, true, item2, no_items2) then update OFS_ITEMS set parent_id = item2.id where id = item1.id; end if; end; -- Copy file or directory. -- File must be unlocked or locked by current OS user and not read_only. -- %param p_itemname \dir1\dir2\file.ext -- %param p_newdir \dir1\dir2 procedure copy(p_itemname in varchar2, p_newdir in varchar2) is item1 OFS_ITEMS%rowtype; no_items1 VC2_array; item2 OFS_ITEMS%rowtype; no_items2 VC2_array; begin if path_to_item(p_itemname, true, item1, no_items1) and path_to_item(p_newdir, true, item2, no_items2) then if item1.item_type = 'D' then create_dir(p_newdir || '\' || item1.name); for c in (select name from OFS_ITEMS where parent_id = item1.id) loop copy(p_itemname || '\' || c.name, p_newdir || '\' || item1.name); end loop; else write_file(p_newdir || '\' || item1.name, item1.data); end if; end if; end; -- Tests if a specified file exists. -- %param p_filename \dir1\dir2\file.ext function file_exists(p_filename in varchar2) return char is item OFS_ITEMS%rowtype; no_items VC2_array; begin if path_to_item(p_filename, false, item, no_items) and (item.item_type = 'F') then return 'Y'; else return 'N'; end if; end; -- Tests if a specified directory exists. -- %param p_path \dir1\dir2 function directory_exists(p_path in varchar2) return char is item OFS_ITEMS%rowtype; no_items VC2_array; begin if path_to_item(p_path, false, item, no_items) and (item.item_type = 'D') then return 'Y'; else return 'N'; end if; end; procedure init_root is i OFS_ITEMS.id%type; begin select id into i from OFS_ITEMS where id = 0; exception when others then insert into OFS_ITEMS (id, parent_id, item_type, name) values (0, 0, 'D', '*'); end; function os_user return varchar2 is begin return ofs_private.os_user; end; end OFS; /