症状
a) 您需要将文件从 SAP 应用程序服务器传输到仅支持 SFTP 协议的接收方。但是,SAP 应用程序服务器只能使用 HTTPS 协议发送文件。此外,您可能希望在发送前对文件进行加密。
b) 您需要通过 HTTPS 将文件从远程服务器传输到 SAP 应用程序服务器。此外,您可能希望在保存之前解密文件。
目前没有标准的程序或工具可用于文件加密、解密和传输。因此,您需要创建一个自定义程序。本说明为您提供示例编码以支持您创建自己的程序。
请注意:PGP 加密不是SAP Employee Central Payroll 服务产品的一部分。原因如下: PGP 需要在应用服务器上安装额外的操作级软件,这在云环境中是不可能的
其他条款
SAP SuccessFactors Employee Central Payroll、SFTP 服务器、文件传输、加密、解密
原因和先决条件
在使用您的自定义程序之前,必须满足以下先决条件:
您有权创建报告程序、从应用程序服务器检索文件以及将它们上传到远程服务器。
在本地服务器上启动用于出站处理的程序的用户对本地文件具有 READ 授权:授权对象 S_DATASET、PROGRAM = SAPLSCMS_FILE、ACTVT = 33(读取)、FILENAME = <本地文件名,包括完整路径>。
请注意:这并不一定意味着该用户可以显示文件内容。例如,如果您既未在程序中使用值帮助,也未授予用户对象 S_TCODE 和 TCD = AL11 的授权,则他们将无法从 UI 访问文件内容。
在 FILE 事务中,您维护了传出和传入文件的逻辑路径和文件名。
在 SM59 事务中,您维护了一个类型为 G =“HTTP 连接到外部服务器”的 RFC 目标,并将远程服务器作为目标主机。您已在“登录和安全”选项卡上维护了远程服务器的用户名和密码。两个系统中必要的基本配置都已完成(例如,在系统中建立可信连接、授权等)。要创建到外部服务器的 HTTPS 连接,Employee Central Payroll 系统必须信任服务器的证书。
要使用 SSL 连接,您需要通过 STRUST 事务附加外部服务器的 CA 证书。示例报告使用默认客户端 PSE(SSL 客户端标准)连接到远程服务器。
您已成功执行 RFC 目标的连接测试。
仅相关,如果您想使用加密功能:
在 STRUST 事务中,您维护了一个 PSE 并导入了您要发送的文件的最终收件人的证书。有关 STRUST 的更多信息,请参阅https://help.sap.com/viewer/2...
解决方案
SAP SuccessFactors 为客户提供对 SFTP 服务器的访问。您可以先通过HTTPS从ABAP应用服务器向这个SFTP服务器发送文件,然后再通过SFTP协议从SFTP服务器传输给接收方。除了使用 SuccessFactors SFTP 服务器,您还可以将文件发送到任何其他通过 HTTPS 协议接受文件的远程服务器。
本笔记所附的示例代码显示,
a) 来自 ABAP 应用程序服务器的文件如何通过 HTTPS 检索、加密和发送到远程服务器
b) 如何通过 HTTPS 检索来自远程服务器的文件、解密并将其存储在 ABAP 应用程序服务器上。
示例程序只为 PSE 和收件人提供一对输入字段。在生产场景中,您不会使用相同的 PSE 和收件人进行加密和解密。但是在您的实施和测试阶段,您可能想要检查是否正确调用了加密功能。为此,输入 PSE 所有者作为接收者(证书主题):只有这样,您才能在同一个 PSE 中同时拥有用于加密的公钥和用于解密的私钥。
如果您对加密或解密有任何问题,请检查系统日志(SM21 事务)以获取有关问题根源的信息。
示例程序使用您在 SM59 事务中维护的 RFC 连接。如果您将目标系统的凭据存储在该 RFC 连接中,您可以安排程序并将其作为后台作业运行:凭据将从 SM59 配置中获取,并且在文件传输期间不会提示您输入它们。
注意:在创建自己的程序时,请特别注意示例代码中以“@CUSTOMER”开头的注释,因为您需要实现自己的逻辑。通过示例程序的流程逻辑,您可以看到如何调用功能(本地读/写、http 文件传输、解密/加密)。
注意:示例编码并非旨在高效使用,SAP 既不提供任何支持,也不对您基于此示例可能实施的任何内容负责。这尤其适用于安全问题:您有责任为您的编码添加安全功能,并相应地配置您的系统(例如,保护您的系统免受病毒侵害)。
注意:示例程序中使用的 SSF_KRN_ENVELOPE 和 SSF_KRN_DEVELOPE 密码功能模块仅支持公钥密码标准 PKCS#7。非对称密钥必须是 RSA 或 ECDSA。支持的对称密码算法请参考 SAP note 2004653的对应部分。如果要显示所有支持的密码标准的列表,请启动事务 SE38,输入Program Name = SSF02并执行 (F8)。在选项部分,选择SSF 格式字段的值帮助以显示该列表。如果您使用SSFW_KRN_ENVELOPE和SSFW_KRN_DEVELOPE 功能模块,您可以使用这些标准中的任何一个加密/解密文件。
要在您自己的系统中实现示例程序,请执行以下操作:
1.在SE38事务中,新建一个程序。例如,ZSAMPLE_CRYPT_TRANSFER_FILE 或选择任何其他名称。
- 在编辑器中,选择Utilities -> More Utilities -> Upload/Download 并将附件(ZSAMPLE_CRYPT_TRANSFER_FILE.txt)上传 到该程序中。
- 激活程序。
- 选择Goto -> Text Elements为每个选择参数和选择屏幕的每个块定义文本。
您会在选择文本的选项卡上找到一些条目:这些条目是在程序激活期间根据选择参数自动生成的,现在您只需填写文本。通过双击程序中块标题的文本符号 text-<xyz>
为文本符号创建条目。适当的文本包含在示例编码中,作为每个选择参数或块标题旁边的注释,您可以简单地将它们从那里复制到选择文本或文本符号中。
提示:您将在示例编码的末尾找到选择屏幕的定义。
- 激活文本。
*&---------------------------------------------------------------------*
REPORT zsample_crypt_transfer_file.
*&---------------------------------------------------------------------*
*&
*& Sample report for
*& a) transferring a file from the application server
*& to an external file server via HTTP(S) protocol
*& Optionally, the file can be encrypted before sending
*& b) fetching a file from an external server via HTTP(S) and
*& saving it to the application server.
*& Optionally, the file can be decrypted before saving
*&
*& !!! Basic example only - coding needs to be adapted by customer!!!
*&
*& For adapting to customer specific needs, pay special attention
*& to comments marked with '@CUSTOMER'
*&
*& !!! MESSAGE and WRITE statements !!!
*& !!! to be used only for testing in dialog mode!!!
*& Define appropriate messages and write to application log
*& in order to run the program in batch mode
*&
*&---------------------------------------------------------------------*
*& M O D I F I C A T I O N L O G *
*&---------------------------------------------------------------------*
*& Date Author Description *
*&---------------------------------------------------------------------*
*& yyyy-mm-dd XXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*
*&
*&---------------------------------------------------------------------*
* ======================================================================
* Local classes
* ======================================================================
* ======================================================================
* lcl_file:
* Represents the file, provides basic operations like SAVE, and
* cryptographic operations
* =====================================================================
CLASS lcl_file DEFINITION FINAL.
PUBLIC SECTION.
TYPES:
tty_line TYPE STANDARD TABLE OF ssfdata .
CONSTANTS gc_mode_orig TYPE c VALUE '1' ##NO_TEXT.
CONSTANTS gc_mode_decrypted TYPE c VALUE '2' ##NO_TEXT.
CONSTANTS gc_mode_encrypted TYPE c VALUE '3' ##NO_TEXT.
CONSTANTS gc_line_length TYPE i VALUE 255 ##NO_TEXT.
METHODS save
IMPORTING
!iv_pathname TYPE fileintern
!iv_mode TYPE flag DEFAULT '2' .
METHODS encrypt
IMPORTING
!iv_pse TYPE ssfappl
!iv_recipient TYPE certsubjct
RAISING
cx_crypto_error .
METHODS decrypt
IMPORTING
!iv_pse TYPE ssfappl
!iv_recipient TYPE certsubjct
RAISING
cx_crypto_error .
METHODS create_from_xstring
IMPORTING
!iv_xstring TYPE xstring
!iv_pathname TYPE string
!iv_bytes TYPE i OPTIONAL .
METHODS load_from_applsrv
IMPORTING
!iv_pathname TYPE fileintern
!iv_filename TYPE string
RETURNING
VALUE(rv_subrc) TYPE i .
METHODS get_mimetype
RETURNING
VALUE(rv_mimetype) TYPE w3conttype .
METHODS get_xstring
IMPORTING
!iv_mode TYPE c
EXPORTING
!ev_xstring TYPE xstring
!ev_length TYPE i
!ev_subrc TYPE sysubrc .
PRIVATE SECTION.
DATA: mv_extension TYPE string,
mt_decrypted_line TYPE tty_line,
mt_encrypted_line TYPE saml2_pse_bin_data_t,
mt_source_line TYPE tty_line,
mv_decrypted_length TYPE i,
mv_encrypted_length TYPE i,
mv_filename TYPE string,
mv_source_length TYPE i,
mv_mimetype TYPE w3conttype.
METHODS set_name_and_type
IMPORTING
!iv_pathname TYPE string .
ENDCLASS.
CLASS lcl_file IMPLEMENTATION.
METHOD create_from_xstring.
* IMPORTING
* !iv_xstring TYPE xstring
* !iv_pathname TYPE string
* !iv_bytes TYPE i OPTIONAL .
" -------------------------------------------------------------
" Creates file content from given xstring and sets some
" instance attributes.
" Method can be used e.g. after retrieving a file via HTTP GET
" -------------------------------------------------------------
DATA:
lv_xstring TYPE xstring,
lv_line TYPE ssfdata.
lv_xstring = iv_xstring.
IF iv_bytes IS INITIAL.
mv_source_length = xstrlen( lv_xstring ).
ELSE.
mv_source_length = iv_bytes.
ENDIF.
WHILE xstrlen( lv_xstring ) > 0.
lv_line = lv_xstring.
APPEND lv_line TO mt_source_line.
SHIFT lv_xstring LEFT BY gc_line_length PLACES IN BYTE MODE.
ENDWHILE.
set_name_and_type( iv_pathname ).
ENDMETHOD.
METHOD decrypt.
* IMPORTING
* !iv_pse TYPE ssfappl
* !iv_recipient TYPE certsubjct
* RAISING
* cx_crypto_error .
" -----------------------------------------------------------------
" Decrypts the binary content of the original file and
" puts the result into mt_decrypted_line
" -----------------------------------------------------------------
FIELD-SYMBOLS:
<ls_source_line> TYPE ssfdata.
DATA:
ls_encrypted_line TYPE ssfbin,
lt_encrypted_line TYPE saml2_pse_bin_data_t,
lv_ssftoolkit TYPE ssftoolkit,
lv_str_format TYPE ssfform,
lv_str_profile TYPE ssfprof,
lv_crc TYPE ssfreturn,
lt_recipient TYPE TABLE OF ssfinfo,
ls_recipient TYPE ssfinfo.
" Before decrypting, we need to re-format the content:
" Decryption works with a structured line type, but
" the original content has a flat line type
LOOP AT mt_source_line ASSIGNING <ls_source_line>.
CLEAR ls_encrypted_line.
ls_encrypted_line-bindata = <ls_source_line>.
APPEND ls_encrypted_line TO lt_encrypted_line.
ENDLOOP.
CALL FUNCTION 'SSF_GET_PARAMETER'
EXPORTING
mandt = sy-mandt
application = iv_pse
IMPORTING
ssftoolkit = lv_ssftoolkit
str_format = lv_str_format
str_profile = lv_str_profile
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0.
WRITE: / 'SSF parameter read failed - see system log (SM21)'.
RAISE EXCEPTION TYPE cx_crypto_error.
ENDIF.
" function module needs a recipient list:
ls_recipient-id = iv_recipient.
ls_recipient-profile = lv_str_profile.
APPEND ls_recipient TO lt_recipient.
CALL FUNCTION 'SSF_KRN_DEVELOPE'
EXPORTING
ssftoolkit = lv_ssftoolkit
str_format = lv_str_format
ostr_enveloped_data_l = mv_source_length
IMPORTING
ostr_output_data_l = mv_decrypted_length
crc = lv_crc
TABLES
ostr_enveloped_data = lt_encrypted_line
recipient = lt_recipient
ostr_output_data = mt_decrypted_line
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0 OR lv_crc <> 0 OR mt_decrypted_line IS INITIAL.
WRITE: / 'Decryption for file ', mv_filename, ' failed - see system log (SM21)'.
RAISE EXCEPTION TYPE cx_crypto_error.
ELSE.
WRITE: / 'File ', mv_filename, ' decrypted'.
ENDIF.
ENDMETHOD.
METHOD encrypt.
* IMPORTING
* !iv_pse TYPE ssfappl
* !iv_recipient TYPE certsubjct
* RAISING
* cx_crypto_error .
" ---------------------------------------------------------------------------
" Encrypts the original binary file content using the given PSE and recipient,
" and puts the result into mt_encrypted_line
" ---------------------------------------------------------------------------
DATA:
lv_ssftoolkit TYPE ssftoolkit,
lv_str_format TYPE ssfform,
lv_str_pab TYPE ssfpab,
lv_str_pab_password TYPE ssfpabpw,
lv_crc TYPE ssfreturn,
lt_recipient TYPE TABLE OF ssfinfo,
ls_recipient TYPE ssfinfo.
CALL FUNCTION 'SSF_GET_PARAMETER'
EXPORTING
mandt = sy-mandt
application = iv_pse
IMPORTING
ssftoolkit = lv_ssftoolkit
str_format = lv_str_format
str_pab = lv_str_pab
str_pab_password = lv_str_pab_password
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0.
WRITE: / 'SSF parameter read failed - see system log (SM21)'.
RAISE EXCEPTION TYPE cx_crypto_error.
ENDIF.
" put the specified recipient into the list:
ls_recipient-id = iv_recipient.
APPEND ls_recipient TO lt_recipient.
CALL FUNCTION 'SSF_KRN_ENVELOPE'
EXPORTING
ssftoolkit = lv_ssftoolkit
str_format = lv_str_format
ostr_input_data_l = mv_source_length
str_pab = lv_str_pab
str_pab_password = lv_str_pab_password
IMPORTING
ostr_enveloped_data_l = mv_encrypted_length
crc = lv_crc
TABLES
ostr_input_data = mt_source_line
recipient_list = lt_recipient
ostr_enveloped_data = mt_encrypted_line
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0 OR lv_crc <> 0 OR mt_encrypted_line IS INITIAL.
WRITE: / 'File ', mv_filename, ': encryption failed - see system log (SM21)'.
RAISE EXCEPTION TYPE cx_crypto_error.
ELSE.
WRITE: / 'File ', mv_filename, ' encrypted'.
ENDIF.
ENDMETHOD.
METHOD get_mimetype.
* RETURNING
* VALUE(rv_mimetype) TYPE w3conttype .
rv_mimetype = mv_mimetype.
ENDMETHOD.
METHOD get_xstring.
* IMPORTING
* !iv_mode TYPE c
* EXPORTING
* !ev_xstring TYPE xstring
* !ev_length TYPE i
* !ev_subrc TYPE sysubrc .
" -------------------------------------------------------------
" Returns the file content as xstring
" Depending on iv_mode, the original, decrypted or encrypted
" content is returned:
" 1 = original
" 2 = decrypted
" 3 = encrypted
" -------------------------------------------------------------
CONSTANTS lc_line_length TYPE i VALUE 255.
FIELD-SYMBOLS <fs_line> TYPE ssfbin.
DATA:
lt_line TYPE tty_line,
lv_length TYPE i,
lv_cumulated_length TYPE i.
CLEAR:
ev_xstring,
ev_length,
ev_subrc.
CASE iv_mode.
WHEN gc_mode_encrypted.
" Line type is structured -> requires special handling
lv_length = mv_encrypted_length.
LOOP AT mt_encrypted_line ASSIGNING <fs_line>.
" append current line at the correct position
CONCATENATE ev_xstring(lv_cumulated_length) <fs_line>-bindata INTO ev_xstring IN BYTE MODE.
lv_cumulated_length = lv_cumulated_length + lc_line_length.
ENDLOOP.
" set xstring to correct length (as provided by the encryption routine)
ev_xstring = ev_xstring(mv_encrypted_length).
ev_length = mv_encrypted_length.
RETURN.
WHEN gc_mode_orig.
lv_length = mv_source_length.
lt_line = mt_source_line.
WHEN gc_mode_decrypted.
lv_length = mv_decrypted_length.
lt_line = mt_decrypted_line.
WHEN OTHERS.
ev_subrc = 1.
RETURN.
ENDCASE.
" Original and decrypted lines both have unstructured line type
CONCATENATE LINES OF lt_line INTO ev_xstring IN BYTE MODE.
" set xstring to correct length:
" wrong length may cause errors in subsequent processing
ev_xstring = ev_xstring(lv_length).
ev_length = lv_length.
ENDMETHOD.
METHOD load_from_applsrv.
* IMPORTING
* !iv_pathname TYPE fileintern
* !iv_filename TYPE string
* RETURNING
* VALUE(rv_subrc) TYPE i .
" -------------------------------------------------------------
" Reads the file content from the specified location on the
" application server and sets some instance attributes
" -------------------------------------------------------------
DATA:
lv_filename(400) TYPE c,
lv_msg TYPE string,
lv_flag TYPE abap_bool,
lv_pathname TYPE string,
lv_message TYPE string.
CLEAR rv_subrc.
" ------------------------------------------------------------------
" !!! Programming guidelines !!!
" Prevent unauthorized file access by using
" logical paths and file names
" ------------------------------------------------------------------
CALL FUNCTION 'FILE_GET_NAME'
EXPORTING
logical_filename = iv_pathname
parameter_1 = iv_filename
IMPORTING
emergency_flag = lv_flag
file_name = lv_pathname
EXCEPTIONS
file_not_found = 1
OTHERS = 2.
rv_subrc = sy-subrc.
IF rv_subrc <> 0 OR lv_flag IS NOT INITIAL.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO lv_message.
WRITE: / lv_message.
WRITE: / 'ERROR in FILE_GET_NAME - file will not be processed.'.
RETURN.
ENDIF.
CALL FUNCTION 'FILE_VALIDATE_NAME'
EXPORTING
logical_filename = iv_pathname
parameter_1 = iv_filename
CHANGING
physical_filename = lv_pathname
EXCEPTIONS
logical_filename_not_found = 1
validation_failed = 2
OTHERS = 3.
rv_subrc = sy-subrc.
IF rv_subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO lv_message.
WRITE: / lv_message.
WRITE: / 'ERROR in FILE_VALIDATE_NAME - file will not be processed.'.
RETURN.
ENDIF.
" Read as BINARY in order to handle all mime types correctly
lv_filename = lv_pathname.
CALL FUNCTION 'SCMS_UPLOAD'
EXPORTING
filename = lv_filename " full name including path
binary = 'X'
frontend = space
IMPORTING
filesize = mv_source_length
TABLES
data = mt_source_line
EXCEPTIONS
error = 1
OTHERS = 2.
rv_subrc = sy-subrc.
IF rv_subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_msg.
WRITE: / lv_msg.
RETURN.
ENDIF.
set_name_and_type( lv_pathname ).
ENDMETHOD.
METHOD save.
* IMPORTING
* !iv_pathname TYPE fileintern
* !iv_mode TYPE flag DEFAULT '2' .
" ----------------------------------------------------------------------
" Saves the file to the target folder on the application server.
" Depending on iv_mode, the original, decrypted or encrypted file
" shall be saved:
" 1 = original
" 2 = decrypted
" 3 = encrypted
" ----------------------------------------------------------------------
DATA:
lv_pathname TYPE string,
lv_line TYPE ssfdata,
lt_line TYPE tty_line,
lv_length TYPE i,
lv_cryptinfo TYPE string,
lv_message TYPE string,
lv_flag TYPE abap_bool.
FIELD-SYMBOLS:
<fs_line> TYPE ssfbin.
" ----------------------------------------------------------------------------
" !!! Programming guidelines !!!
" Prevent unauthorized file access by using
" logical paths and file names
" ----------------------------------------------------------------------------
CALL FUNCTION 'FILE_GET_NAME'
EXPORTING
logical_filename = iv_pathname
parameter_1 = mv_filename
IMPORTING
emergency_flag = lv_flag
file_name = lv_pathname
EXCEPTIONS
file_not_found = 1
OTHERS = 2.
IF sy-subrc <> 0 OR lv_flag IS NOT INITIAL.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO lv_message.
WRITE: / lv_message.
WRITE: / 'ERROR in FILE_GET_NAME - file will not be stored.'.
RETURN.
ENDIF.
CALL FUNCTION 'FILE_VALIDATE_NAME'
EXPORTING
logical_filename = iv_pathname
parameter_1 = mv_filename
CHANGING
physical_filename = lv_pathname
EXCEPTIONS
logical_filename_not_found = 1
validation_failed = 2
OTHERS = 3.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO lv_message.
WRITE: / lv_message.
WRITE: / 'ERROR in FILE_VALIDATE_NAME - file will not be stored.'.
RETURN.
ENDIF.
" consolidate different data tables and formats
CASE iv_mode.
WHEN gc_mode_encrypted.
lv_cryptinfo = 'Encrypted file '.
" Line type is structured -> requires special handling
lv_length = mv_encrypted_length.
LOOP AT mt_encrypted_line ASSIGNING <fs_line>.
APPEND <fs_line>-bindata TO lt_line.
ENDLOOP.
WHEN gc_mode_orig.
lv_cryptinfo = 'Original file '.
lv_length = mv_source_length.
lt_line = mt_source_line.
WHEN gc_mode_decrypted.
lv_cryptinfo = 'Decrypted file '.
lv_length = mv_decrypted_length.
lt_line = mt_decrypted_line.
WHEN OTHERS.
RETURN.
ENDCASE.
IF lv_length > 0.
" Could be 0 e.g. if you are trying to save the decrypted version of
" a file that has never been decrypted.
OPEN DATASET lv_pathname FOR OUTPUT IN BINARY MODE.
LOOP AT lt_line INTO lv_line.
IF lv_length LT gc_line_length. "255.
TRANSFER lv_line(lv_length) TO lv_pathname.
EXIT. "from LOOP
ELSE.
TRANSFER lv_line TO lv_pathname.
lv_length = lv_length - gc_line_length.
ENDIF.
ENDLOOP.
CLOSE DATASET lv_pathname.
WRITE: / lv_cryptinfo, ' saved: ', lv_pathname.
ELSE.
WRITE: / lv_cryptinfo, mv_filename, ' is empty - not saved'.
ENDIF.
ENDMETHOD.
METHOD set_name_and_type.
* IMPORTING
* !iv_pathname TYPE string .
" ----------------------------------------------
" Determines file name and mime type
" and stores them in instance attributes
" ----------------------------------------------
DATA:
lt_string TYPE string_table,
lv_count TYPE i,
lv_extension TYPE w3fileext.
CALL FUNCTION 'TRINT_SPLIT_FILE_AND_PATH'
EXPORTING
full_name = iv_pathname
IMPORTING
stripped_name = mv_filename
EXCEPTIONS
x_error = 1
OTHERS = 2.
IF sy-subrc <> 0.
WRITE: /'ERROR when splitting path.'.
RETURN.
ENDIF.
CLEAR lt_string.
SPLIT mv_filename AT '.' INTO TABLE lt_string.
DESCRIBE TABLE lt_string LINES lv_count.
IF lv_count GT 1.
READ TABLE lt_string INTO mv_extension INDEX lv_count.
ENDIF.
" string variable mv_extension cannot be passed into the function module
" SDOK_MIMETYPE_GET will set a default if there is no extension
lv_extension = mv_extension.
CALL FUNCTION 'SDOK_MIMETYPE_GET'
EXPORTING
extension = lv_extension
IMPORTING
mimetype = mv_mimetype.
ENDMETHOD.
ENDCLASS.
* ======================================================================
* lcl_file_http:
* Utility class for http file transfer in both directions
* ======================================================================
CLASS lcl_file_http DEFINITION FINAL.
PUBLIC SECTION.
METHODS constructor
IMPORTING
!iv_rfc_dest TYPE rfcdest
RAISING
!cx_dest_http_abap_special
.
METHODS send
IMPORTING
!iv_target_folder TYPE string
!iv_file_name TYPE string
!iv_content_type TYPE string
!iv_content TYPE xstring
!iv_bytes TYPE i.
METHODS get
IMPORTING
!iv_source_path TYPE string
EXPORTING
!ev_content TYPE xstring
!ev_bytes TYPE i
!ev_subrc TYPE sysubrc .
PRIVATE SECTION.
DATA mr_client TYPE REF TO if_http_client .
CONSTANTS mc_protocol TYPE string VALUE 'HTTP/1.1' ##NO_TEXT.
METHODS set_uri
IMPORTING
!iv_file_name TYPE string
!iv_target_folder TYPE string
RETURNING
VALUE(rv_uri) TYPE string .
ENDCLASS.
CLASS lcl_file_http IMPLEMENTATION.
METHOD constructor.
* IMPORTING
* !iv_rfc_dest TYPE rfcdest
* RAISING
* !cx_dest_http_abap_special
DATA: lv_rfc_dest TYPE char32.
lv_rfc_dest = iv_rfc_dest.
" create http client for file transfer
CALL METHOD cl_http_client=>create_by_destination
EXPORTING
destination = lv_rfc_dest
IMPORTING
client = mr_client
EXCEPTIONS
argument_not_found = 1
destination_not_found = 2
destination_no_authority = 3
plugin_not_active = 4
internal_error = 5
OTHERS = 6.
IF sy-subrc IS NOT INITIAL.
" not semantically fitting, but nothing better available in package SHTTP
RAISE EXCEPTION TYPE cx_dest_http_abap_special.
ENDIF.
ENDMETHOD.
METHOD get.
* IMPORTING
* !iv_source_path TYPE string
* EXPORTING
* !ev_content TYPE xstring
* !ev_bytes TYPE i
* !ev_subrc TYPE sysubrc .
" ----------------------------------------------------------------
" Sets request attributes,
" gets file from the remote server,
" and handles errors
" ----------------------------------------------------------------
DATA: lv_xstring TYPE xstring,
lv_path TYPE string,
lr_http_entity TYPE REF TO if_http_entity,
lv_code TYPE i,
lv_reason TYPE string.
CLEAR ev_subrc.
CLEAR ev_bytes.
CLEAR ev_content.
CALL METHOD mr_client->refresh_request
EXCEPTIONS
http_action_failed = 1
OTHERS = 2.
IF sy-subrc <> 0.
WRITE: / 'Could not refresh HTTP client - processing terminated.'.
RETURN.
ENDIF.
CONCATENATE '/' iv_source_path INTO lv_path.
CALL METHOD mr_client->request->set_header_field
EXPORTING
name = if_http_header_fields_sap=>request_uri
value = mr_client->create_abs_url( path = lv_path ).
CALL METHOD mr_client->request->set_header_field
EXPORTING
name = if_http_header_fields_sap=>request_method
value = 'GET'.
CALL METHOD mr_client->request->set_header_field
EXPORTING
name = if_http_header_fields_sap=>server_protocol
value = mc_protocol.
CALL METHOD mr_client->send
EXPORTING
timeout = 0
EXCEPTIONS
OTHERS = 0.
CALL METHOD mr_client->receive
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3
OTHERS = 4.
IF sy-subrc NE 0.
ev_subrc = sy-subrc.
WRITE:/ 'ERROR for file ', iv_source_path, ': GET failed'.
WRITE:/ ' Reason: '.
CASE ev_subrc.
WHEN 1.
WRITE 'http communication failure'.
WHEN 2.
WRITE 'http invalid state'.
WHEN 3.
WRITE 'http processing failed'.
WHEN OTHERS.
WRITE 'not specified'.
ENDCASE.
RETURN.
ENDIF.
" Check the status of the response:
" Previous method calls may not have raised an exception
" even though the GET was not successful (e.g. if the file was not found)
CALL METHOD mr_client->response->get_status
IMPORTING
code = lv_code
reason = lv_reason.
IF lv_code = '200'. " 200 = everything OK
lr_http_entity ?= mr_client->response.
" -----------------------------------------------------------------------
" @CUSTOMER:
" !!! IMPORTANT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
" !!! Activate the virus scan when retrieving data from the remote server
" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
" -----------------------------------------------------------------------
CALL METHOD lr_http_entity->get_data
EXPORTING
vscan_scan_always = if_http_entity=>co_content_check_always " !!! switch on virus scan !!!
RECEIVING
data = lv_xstring.
IF lv_xstring IS INITIAL.
ev_subrc = 1.
WRITE: / 'ERROR: method GET_DATA returned an empty xstring for file ', iv_source_path.
RETURN.
ENDIF.
ev_content = lv_xstring.
ev_bytes = xstrlen( lv_xstring ).
ELSE.
WRITE:/ 'HTTP ERROR for file: ', iv_source_path, ', Code: ', lv_code, ', Reason: ', lv_reason.
ev_subrc = 1.
ENDIF.
ENDMETHOD. "get
METHOD send.
* IMPORTING
* !iv_target_folder TYPE string
* !iv_file_name TYPE string
* !iv_content_type TYPE string
* !iv_content TYPE xstring
* !iv_bytes TYPE i.
" ----------------------------------------------------------------
" Sets request attributes,
" sends file to the remote server,
" and handles errors
" ----------------------------------------------------------------
DATA:
lv_code TYPE i,
lv_reason TYPE string.
CALL METHOD mr_client->refresh_request
EXCEPTIONS
http_action_failed = 1
OTHERS = 2.
IF sy-subrc <> 0.
WRITE: / 'Could not refresh HTTP client - processing terminated.'.
RETURN.
ENDIF.
CALL METHOD mr_client->request->set_header_field
EXPORTING
name = if_http_header_fields_sap=>request_uri
value = set_uri( iv_target_folder = iv_target_folder
iv_file_name = iv_file_name ).
CALL METHOD mr_client->request->set_header_field
EXPORTING
name = if_http_header_fields_sap=>request_method
value = 'PUT'.
CALL METHOD mr_client->request->set_header_field
EXPORTING
name = if_http_header_fields_sap=>server_protocol
value = mc_protocol.
CALL METHOD mr_client->request->set_header_field
EXPORTING
name = 'content-type'
value = iv_content_type.
CALL METHOD mr_client->request->set_data
EXPORTING
data = iv_content
length = iv_bytes.
CALL METHOD mr_client->send
EXPORTING
timeout = 0.
CALL METHOD mr_client->receive
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3
OTHERS = 4.
IF sy-subrc NE 0.
WRITE:/ 'ERROR for file ', iv_file_name, ': creation of remote file failed'.
WRITE:/ ' Reason: '.
CASE sy-subrc.
WHEN 1.
WRITE 'http communication failure'.
WHEN 2.
WRITE 'http invalid state'.
WHEN 3.
WRITE 'http processing failed'.
WHEN OTHERS.
WRITE 'not specified'.
ENDCASE.
ELSE.
CALL METHOD mr_client->response->get_status
IMPORTING
code = lv_code
reason = lv_reason.
IF lv_code = '201'. " 201 = Created
WRITE: / 'File ', iv_file_name, ' sent.'.
ELSE.
WRITE:/ 'HTTP ERROR when sending file: ', iv_file_name, ', Code: ', lv_code, ', Reason: ', lv_reason.
ENDIF.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method LCL_FILE_http->SET_URI
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_FILE_NAME TYPE STRING
* | [--->] IV_TARGET_FOLDER TYPE STRING
* | [<-()] RV_URI TYPE STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD set_uri.
* IMPORTING
* !iv_file_name TYPE string
* !iv_target_folder TYPE string
* RETURNING
* VALUE(rv_uri) TYPE string .
" Sets the full URI for the file in the target system
DATA lv_path TYPE string.
CONCATENATE '/' iv_target_folder '/' iv_file_name INTO lv_path.
CALL METHOD mr_client->create_abs_url
EXPORTING
path = lv_path
RECEIVING
url = rv_uri.
ENDMETHOD.
ENDCLASS.
* ======================================================================
* lcl_sample:
* Handles the processing according to the parameters set
* by the user on the selection screen of the report
* ======================================================================
CLASS lcl_sample DEFINITION FINAL.
PUBLIC SECTION.
METHODS main
IMPORTING
!iv_dest TYPE rfcdest
!iv_out TYPE abap_bool
!iv_multi TYPE abap_bool
!iv_lsrc TYPE string
!iv_rsrc TYPE string
!iv_rfold TYPE string
!iv_encr TYPE abap_bool
!iv_decr TYPE abap_bool
!iv_pse TYPE ssfappl
!iv_rec TYPE certsubjct
.
METHODS process_outbound
IMPORTING
!iv_multi TYPE abap_bool
!iv_lsrc TYPE string
!iv_rfold TYPE string
!iv_encr TYPE abap_bool
!iv_pse TYPE ssfappl
!iv_rec TYPE certsubjct
.
METHODS process_inbound
IMPORTING
!iv_rsrc TYPE string
!iv_decr TYPE abap_bool
!iv_pse TYPE ssfappl
!iv_rec TYPE certsubjct
.
METHODS get_file_list
IMPORTING
!iv_multi TYPE abap_bool
!iv_lsrc TYPE string
EXPORTING
!et_filename TYPE string_table
.
PRIVATE SECTION.
***************************************************************************
*** @CUSTOMER:
*** Use logical file names in order to prevent directory traversal.
*** The user only has the possibility to select the file name, but not
*** the directory on the application server.
*** If you want to have some flexibility in the names of the actual files,
*** customize your logical file names such that the name of the physical
*** file is used as (part of) the logical file name.
*** Otherwise, simply use hard-coded file names as well, and do not offer
*** the user the possibility to enter them.
***
*** Use transaction FILE to maintain logical paths and file names
***************************************************************************
CONSTANTS: gc_logical_fname_inbound TYPE fileintern VALUE 'ZSAMPLE_INBOUND',
gc_logical_fname_outbound TYPE fileintern VALUE 'ZSAMPLE_OUTBOUND'.
DATA: mo_file_http TYPE REF TO lcl_file_http.
ENDCLASS.
CLASS lcl_sample IMPLEMENTATION.
METHOD main.
* IMPORTING
* !iv_dest TYPE rfcdest
* !iv_out type abap_bool
* !iv_multi TYPE abap_bool
* !iv_lsrc TYPE string
* !iv_rsrc TYPE string
* !iv_rfold TYPE string
* !iv_encr TYPE abap_bool
* !iv_decr TYPE abap_bool
* !iv_pse TYPE ssfappl
* !iv_rec TYPE certsubjct
* .
TRY.
CREATE OBJECT mo_file_http
EXPORTING
iv_rfc_dest = iv_dest.
CATCH cx_dest_http_abap_special.
MESSAGE e016(pg) WITH 'Error in http client creation' ' - processing terminated'.
RETURN.
ENDTRY.
IF iv_out = abap_true.
CALL METHOD me->process_outbound
EXPORTING
iv_multi = iv_multi
iv_lsrc = iv_lsrc
iv_rfold = iv_rfold
iv_encr = iv_encr
iv_pse = iv_pse
iv_rec = iv_rec.
ELSE.
CALL METHOD me->process_inbound
EXPORTING
iv_rsrc = iv_rsrc
iv_decr = iv_decr
iv_pse = iv_pse
iv_rec = iv_rec.
ENDIF.
WRITE: / 'Processing finished.'.
ENDMETHOD.
METHOD process_outbound.
* !iv_multi TYPE abap_bool
* !iv_lsrc TYPE string
* !iv_rfold TYPE string
* !iv_encr TYPE abap_bool
* !iv_pse TYPE ssfappl
* !iv_rec TYPE certsubjct
" ---------------------------------------------------------
" Read files from the application server,
" encrypt them if requested,
" and send them to the remote server
" ---------------------------------------------------------
DATA:
lt_filename TYPE string_table,
lr_file TYPE REF TO lcl_file,
lv_rc TYPE sysubrc,
lv_xstring TYPE xstring,
lv_bytes TYPE i
.
FIELD-SYMBOLS:
<lv_filename> TYPE string.
CALL METHOD get_file_list
EXPORTING
iv_multi = iv_multi
iv_lsrc = iv_lsrc
IMPORTING
et_filename = lt_filename. " file names without path
IF lt_filename IS INITIAL.
WRITE: / 'No files in local source directory - nothing to be sent'.
RETURN.
ENDIF.
LOOP AT lt_filename ASSIGNING <lv_filename>.
CREATE OBJECT lr_file.
" Load file from application server
" Logical file name is checked inside load_from_applsrv
CALL METHOD lr_file->load_from_applsrv
EXPORTING
iv_pathname = gc_logical_fname_outbound
iv_filename = <lv_filename>
RECEIVING
rv_subrc = lv_rc.
IF lv_rc <> 0.
WRITE: / 'Could not load file ', <lv_filename>, ' - skipping it'.
CONTINUE.
ENDIF.
IF iv_encr = abap_true.
TRY.
CALL METHOD lr_file->encrypt
EXPORTING
iv_pse = iv_pse
iv_recipient = iv_rec.
CATCH cx_crypto_error .
WRITE: / 'Error in encryption - ' , ' processing terminated'.
RETURN.
ENDTRY.
" use encrypted content for sending
CALL METHOD lr_file->get_xstring
EXPORTING
iv_mode = lcl_file=>gc_mode_encrypted
IMPORTING
ev_xstring = lv_xstring
ev_length = lv_bytes
ev_subrc = lv_rc.
ELSE.
" use original content for sending
CALL METHOD lr_file->get_xstring
EXPORTING
iv_mode = lcl_file=>gc_mode_orig
IMPORTING
ev_xstring = lv_xstring
ev_length = lv_bytes
ev_subrc = lv_rc.
ENDIF.
IF lv_rc <> 0.
CONTINUE.
ENDIF.
CALL METHOD mo_file_http->send
EXPORTING
iv_target_folder = iv_rfold
iv_file_name = <lv_filename>
iv_content_type = lr_file->get_mimetype( )
iv_content = lv_xstring
iv_bytes = lv_bytes.
ENDLOOP.
ENDMETHOD.
METHOD process_inbound.
* IMPORTING
* !iv_rsrc TYPE string
* !iv_decr TYPE abap_bool
* !iv_pse TYPE ssfappl
* !iv_rec TYPE certsubjct
" ---------------------------------------------------------
" Retrieve file from the remote server,
" decrypt it if requested,
" and store it on the application server
" ---------------------------------------------------------
DATA:
lr_file TYPE REF TO lcl_file,
lv_content TYPE xstring,
lv_bytes TYPE i,
lv_rc TYPE sysubrc,
lv_mode TYPE c.
CALL METHOD mo_file_http->get
EXPORTING
iv_source_path = iv_rsrc
IMPORTING
ev_content = lv_content
ev_bytes = lv_bytes
ev_subrc = lv_rc.
IF lv_rc IS NOT INITIAL OR lv_bytes IS INITIAL.
WRITE: / 'Remote file could not be retrieved.'.
RETURN.
ENDIF.
CREATE OBJECT lr_file.
CALL METHOD lr_file->create_from_xstring
EXPORTING
iv_xstring = lv_content
iv_pathname = iv_rsrc
iv_bytes = lv_bytes.
IF iv_decr = abap_true.
TRY.
CALL METHOD lr_file->decrypt
EXPORTING
iv_pse = iv_pse
iv_recipient = iv_rec.
CATCH cx_crypto_error .
WRITE: / 'Error in decryption - ' , ' file will not be saved'.
RETURN.
ENDTRY.
lv_mode = lcl_file=>gc_mode_decrypted.
ELSE.
lv_mode = lcl_file=>gc_mode_orig.
ENDIF.
CALL METHOD lr_file->save
EXPORTING
iv_pathname = gc_logical_fname_inbound
iv_mode = lv_mode.
ENDMETHOD.
METHOD get_file_list.
* IMPORTING
* !iv_multi TYPE abap_bool
* !iv_lsrc TYPE string
* EXPORTING
* !et_filename TYPE string_table
" -----------------------------------------------------------------------
" Gets the list of file names (without path)
" for the files that are to be processed
" -----------------------------------------------------------------------
DATA:
lv_message TYPE string,
lv_flag TYPE abap_bool,
lv_dirname TYPE epsdirnam,
lt_dir_list TYPE TABLE OF epsfili,
lv_filename TYPE string
.
FIELD-SYMBOLS:
<ls_dir_list> TYPE epsfili
.
CLEAR et_filename.
IF iv_multi = abap_true.
" Get the names of all usable files in the logical path
" @CUSTOMER:
" This function call gives us the physical path to the
" logical file name - requires appropriate setup in
" transaction FILE
CALL FUNCTION 'FILE_GET_NAME'
EXPORTING
logical_filename = gc_logical_fname_outbound
IMPORTING
emergency_flag = lv_flag
file_name = lv_dirname " physical path
EXCEPTIONS
file_not_found = 1
OTHERS = 2.
IF sy-subrc <> 0 OR lv_flag IS NOT INITIAL.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO lv_message.
WRITE: / lv_message.
WRITE: / 'Logical path ', gc_logical_fname_outbound, ' cannot be read - outbound processing cancelled.'.
RETURN.
ENDIF.
CALL FUNCTION 'EPS_GET_DIRECTORY_LISTING'
EXPORTING
dir_name = lv_dirname
TABLES
dir_list = lt_dir_list " file names without path, plus size
EXCEPTIONS
invalid_eps_subdir = 1
sapgparam_failed = 2
build_directory_failed = 3
no_authorization = 4
read_directory_failed = 5
too_many_read_errors = 6
empty_directory_list = 7
OTHERS = 8.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO lv_message.
WRITE: / lv_message.
WRITE: / 'Physical path for ', gc_logical_fname_outbound, ' cannot be accessed - outbound processing cancelled.'.
RETURN.
ENDIF.
LOOP AT lt_dir_list ASSIGNING <ls_dir_list>.
CHECK <ls_dir_list>-rc = 0 AND
<ls_dir_list>-size > 0 AND
<ls_dir_list>-name(4) <> 'core'.
lv_filename = <ls_dir_list>-name.
APPEND lv_filename TO et_filename.
ENDLOOP.
ELSE.
" only one file (name entered by the user)
APPEND iv_lsrc TO et_filename.
ENDIF. " IF iv_multi = abap_true
ENDMETHOD.
ENDCLASS.
* ==================================================================
* The report itself
* ==================================================================
" ------------------------------------------------------------------
" GLOBAL VARIABLES
" Mostly needed for handling screen fields (properties and content)
" ------------------------------------------------------------------
DATA: go_sample TYPE REF TO lcl_sample ##NEEDED,
gv_program TYPE sy-repid ##NEEDED,
gt_fieldvalue TYPE STANDARD TABLE OF rsselread ##NEEDED,
gs_fieldvalue TYPE rsselread ##NEEDED,
gv_encr TYPE abap_bool ##NEEDED, " encrypt?
gv_decr TYPE abap_bool ##NEEDED, " decrypt?
gv_rsrc TYPE string ##NEEDED, " remote source file
gv_lsrc TYPE string ##NEEDED, " local source file
gv_rfold TYPE string ##NEEDED, " remote folder
gv_multi TYPE abap_bool ##NEEDED. "all files from folder?
*********************************************************************
*** @CUSTOMER:
*** Selections are mainly for testing purposes.
*** Depending on your use case you may eliminate
*** some - or even all - selection parameters
*** For example, if you always send the same file
*** to the same destination, you can hard-code all these parameters.
*********************************************************************
" ------------------------------------------------------------------
" SELECTION SCREEN
" ------------------------------------------------------------------
" Settings for both inbound and outbound processing
SELECTION-SCREEN: BEGIN OF BLOCK a01 WITH FRAME TITLE text-a01. "General Settings
PARAMETERS: p_dest TYPE rfcdest MATCHCODE OBJECT esh_h_rfcdest_g OBLIGATORY. "RFC destination (as in SM59)
SELECTION-SCREEN SKIP 1.
SELECTION-SCREEN: BEGIN OF BLOCK a02 WITH FRAME TITLE text-a02. "Encryption/Decryption
PARAMETERS: p_pse TYPE ssfappl MATCHCODE OBJECT f4strustssf. "SSF Application (PSE)
PARAMETERS: p_rec TYPE certsubjct. "Recipient (Cert. Subject)
SELECTION-SCREEN: END OF BLOCK a02.
SELECTION-SCREEN: END OF BLOCK a01.
SELECTION-SCREEN SKIP 1.
" Outbound processing only
PARAMETERS: p_out RADIOBUTTON GROUP m1 DEFAULT 'X'. "Outbound Processing 出站处理
SELECTION-SCREEN: BEGIN OF BLOCK b01 WITH FRAME.
SELECTION-SCREEN: BEGIN OF BLOCK b02 WITH FRAME TITLE text-b02. "Selection Mode
PARAMETERS: p_single RADIOBUTTON GROUP g1 DEFAULT 'X', "Single file 单个选择文件
p_lsrc TYPE string, "Local Source File 本地源文件
p_multi RADIOBUTTON GROUP g1. "All Files from Folder 文件夹中的所有文件
SELECTION-SCREEN: END OF BLOCK b02.
PARAMETERS:
p_rfold TYPE string LOWER CASE, "Remote Target Folder 远程目标文件夹
p_encr AS CHECKBOX. "Encrypt before sending 发送前加密
SELECTION-SCREEN: END OF BLOCK b01.
SELECTION-SCREEN SKIP 1.
" Inbound processing only
PARAMETERS: p_in RADIOBUTTON GROUP m1. "Inbound Processing 入站处理
SELECTION-SCREEN: BEGIN OF BLOCK c02 WITH FRAME.
PARAMETERS: p_rsrc TYPE string LOWER CASE, "Remote Source File 远程源文件
p_decr AS CHECKBOX. "Decrypt before saving 保存前解密
SELECTION-SCREEN: END OF BLOCK c02.
INITIALIZATION.
" ----------------------------------------------------------
" @CUSTOMER:
" You must not use this sample coding 'as is'
" The program cannot be executed unless you make changes
" to the coding you copied from the sample
" ----------------------------------------------------------
*“您不能使用“按原样”的示例编码”
*“除非进行更改,否则无法执行该程序
*“复制到您从样本中复制的编码
*" ---------------------------------------------
MESSAGE e016(pg) WITH '>>> This is only sample coding <<< ' '>>> Please adjust it to your needs <<<'.
" ------------------------------------------------------------------
" SCREEN EVENTS
" ------------------------------------------------------------------
" ------------------------------------------------------------
" Control the selection screen
" ------------------------------------------------------------
AT SELECTION-SCREEN OUTPUT.
" disable irrelevant fields, depending on outbound/inbound direction
PERFORM toggle_fields.
AT SELECTION-SCREEN ON RADIOBUTTON GROUP m1.
" When user toggles between outbound and inbound:
" 1) get content of fields for both directions
" 2) disable fields for the direction that has not been selected
" 3) preserve existing field content such that it is still available
" if the user toggles back
*“当用户在出站和入站之间切换时:
*“1)获取两个方向的字段内容
*“2)禁用未选择方向的字段
*“3)保留现有字段内容,使其仍然可用
*“如果用户切换回
PERFORM get_field_content.
PERFORM toggle_fields.
PERFORM set_field_content.
" ----------------------------------------------------------
" Check input consistency
" ----------------------------------------------------------
AT SELECTION-SCREEN.
IF p_out IS NOT INITIAL AND p_rfold IS INITIAL.
MESSAGE e016(pg) WITH 'Please specify ' 'target folder for' 'outbound processing.'.
ENDIF.
IF p_out IS INITIAL AND p_rsrc IS INITIAL .
MESSAGE e016(pg) WITH 'Please specify' 'source file for' 'inbound processing.'.
ENDIF.
IF ( p_pse IS INITIAL OR p_rec IS INITIAL ) AND
( ( p_out IS NOT INITIAL AND p_encr IS NOT INITIAL ) OR
( p_out IS INITIAL AND p_decr IS NOT INITIAL ) ).
MESSAGE e016(pg) WITH 'Please specify' ' PSE and recipient for ' ' de-/encryption'.
ENDIF.
" ----------------------------------------------------------
" Trigger send/receive functionality
" ----------------------------------------------------------
START-OF-SELECTION.
CREATE OBJECT go_sample.
CALL METHOD go_sample->main
EXPORTING
iv_dest = p_dest
iv_out = p_out
iv_multi = p_multi
iv_lsrc = p_lsrc
iv_rsrc = p_rsrc
iv_rfold = p_rfold
iv_encr = p_encr
iv_decr = p_decr
iv_pse = p_pse
iv_rec = p_rec.
*&---------------------------------------------------------------------*
*& Form TOGGLE_FIELDS
*&---------------------------------------------------------------------*
* Enable or disable fields depending on selection of
* outbound / inbound processing
*----------------------------------------------------------------------*
FORM toggle_fields .
LOOP AT SCREEN.
IF p_out IS NOT INITIAL.
IF screen-name = 'P_RSRC' OR
screen-name = 'P_DECR'.
screen-input = 0.
ELSE.
IF screen-name = 'P_SINGLE' OR
screen-name = 'P_MULTI' OR
screen-name = 'P_RFOLD' OR
screen-name = 'P_ENCR'.
screen-input = 1.
ENDIF.
IF screen-name = 'P_LSRC'. "name of single file
IF p_single IS NOT INITIAL.
screen-input = 1.
ELSE.
screen-input = 0.
ENDIF.
ENDIF.
ENDIF.
MODIFY SCREEN.
ELSE.
IF screen-name = 'P_RSRC' OR
screen-name = 'P_DECR'.
screen-input = 1.
ELSEIF screen-name = 'P_SINGLE' OR
screen-name = 'P_MULTI' OR
screen-name = 'P_LSRC' OR
screen-name = 'P_RFOLD' OR
screen-name = 'P_ENCR'.
screen-input = 0.
ENDIF.
MODIFY SCREEN.
ENDIF.
ENDLOOP.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form GET_FIELD_CONTENT
*&---------------------------------------------------------------------*
* Get field content from selection screen such that
* it can be preserved when user toggles between
* outbound and inbound
*----------------------------------------------------------------------*
FORM get_field_content .
gv_program = sy-repid.
CLEAR gt_fieldvalue.
" fields for outbound processing
gs_fieldvalue-kind = 'P'. "selection parameter
gs_fieldvalue-name = 'P_MULTI'.
APPEND gs_fieldvalue TO gt_fieldvalue.
gs_fieldvalue-name = 'P_LSRC'.
APPEND gs_fieldvalue TO gt_fieldvalue.
gs_fieldvalue-name = 'P_RFOLD'.
APPEND gs_fieldvalue TO gt_fieldvalue.
gs_fieldvalue-name = 'P_ENCR'.
APPEND gs_fieldvalue TO gt_fieldvalue.
" fields for inbound processing
gs_fieldvalue-name = 'P_RSRC'.
APPEND gs_fieldvalue TO gt_fieldvalue.
gs_fieldvalue-name = 'P_DECR'.
APPEND gs_fieldvalue TO gt_fieldvalue.
CALL FUNCTION 'RS_SELECTIONSCREEN_READ'
EXPORTING
program = gv_program
TABLES
fieldvalues = gt_fieldvalue.
" outbound
READ TABLE gt_fieldvalue INTO gs_fieldvalue WITH KEY name = 'P_MULTI'.
IF sy-subrc = 0.
gv_multi = gs_fieldvalue-fieldvalue.
ENDIF.
READ TABLE gt_fieldvalue INTO gs_fieldvalue WITH KEY name = 'P_LSRC'.
IF sy-subrc = 0.
gv_lsrc = gs_fieldvalue-fieldvalue.
ENDIF.
READ TABLE gt_fieldvalue INTO gs_fieldvalue WITH KEY name = 'P_RFOLD'.
IF sy-subrc = 0.
gv_rfold = gs_fieldvalue-fieldvalue.
ENDIF.
READ TABLE gt_fieldvalue INTO gs_fieldvalue WITH KEY name = 'P_ENCR'.
IF sy-subrc = 0.
gv_encr = gs_fieldvalue-fieldvalue.
ENDIF.
" inbound
READ TABLE gt_fieldvalue INTO gs_fieldvalue WITH KEY name = 'P_RSRC'.
IF sy-subrc = 0.
gv_rsrc = gs_fieldvalue-fieldvalue.
ENDIF.
READ TABLE gt_fieldvalue INTO gs_fieldvalue WITH KEY name = 'P_DECR'.
IF sy-subrc = 0.
gv_decr = gs_fieldvalue-fieldvalue.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form SET_FIELD_CONTENT
*&---------------------------------------------------------------------*
* Set field content to previous values (after user
* has toggled between outbound and inbound)
*----------------------------------------------------------------------*
FORM set_field_content .
" outbound
p_rfold = gv_rfold.
p_multi = gv_multi.
p_lsrc = gv_lsrc.
p_encr = gv_encr.
" inbound
p_rsrc = gv_rsrc.
p_decr = gv_decr.
ENDFORM.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。