. FTP 서버 관리

HTTP는 읽기전용의 프로토콜이므로 URL만 알면 누구나 바로 접속할 수 있고 파일을 가져올 수 있다. 반면 FTP는 읽기와 쓰기는 물론이고 폴더 생성, 파일 삭제까지도 가능하므로 허가된 사용자에 대해서만 접속을 허용한다. 그래서 접속 과정이 HTTP보다 더 복잡하며 사용자 ID, 비밀번호, 접속 포트 번호 등 접속에 필요한 정보들이 많다. 쓰기를 허용하는 프로토콜이다 보니 보안에 민감할 수밖에 없다.

FTP로부터 파일을 가져올 때마다 접속에 필요한 이러한 정보를 일일이 입력하는 것은 번거로운 일이므로 자주 접속하는 FTP 서버는 접속정보를 미리 등록해놓고 이 정보들에 이름을 주어 접속명으로 관리하도록 한다. 사용자들은 접속할 서버의 정보를 한 번만 입력하며 FTP 클라이언트가 이 정보를 기억함으로써 다음 접속할 때는 접속명으로 신속하게 접속할 수 있게 된다. FTP 서버 관리를 위해 다음 구조체를 Util.h에 선언하도록 하자.

 

struct FtpInfo

{

     TCHAR Name[32];

     TCHAR Addr[256];

     TCHAR User[32];

     TCHAR Pass[32];

     int Port;

};

 

Name은 접속할 서버에 대한 간단한 이름을 주는 접속명이며 서버간의 구분을 위해서 주는 이름이므로 우리 회사, 개똥이 써~같이 알아보기 편리한 임의의 이름을 주면 된다. Addr은 접속할 서버의 실제 주소이며 User는 사용자 이름, Pass는 비밀 번호이다. Port FTP 서버가 사용하는 소켓 포트 번호인데 통상 FTP 서버는 21번 포트를 사용한다. FTP 서버 목록은 한 번 등록하면 계속 기억해야 하므로 SOption 구조체에 포함시키도록 하자.

 

struct SOption

{

     ....

     FtpInfo arFtp[32];

 

서버 개수를 무한정으로 기억할 수는 없으므로 크기 32의 배열을 선언하였다. arFtp 배열의 제일 마지막 요소는 배열 끝을 나타내는 용도로 사용되므로 실제로 등록 가능한 서버의 개수는 31개가 된다. Name 멤버가 빈 문자열을 가지면 여기를 배열의 끝이라고 판단한다. 생성자에서 arFtp배열의 모든 Name 멤버에 빈 문자열을 대입하여 최초 등록된 서버가 없도록 하였다.

 

SOption::SOption()

{

     int i;

     for (i=0;i<sizeof(arFtp)/sizeof(arFtp[0]);i++) {

          lstrcpy(arFtp[i].Name,"");

     }

     Init();

}

 

arFtp 배열 초기화를 SOption::Init에서 하지 않고 생성자에서 하는 이유는 권장옵션으로 돌아가더라도 등록된 FTP 서버는 삭제되지 말아야 하기 때문이다. 등록된 FTP 서버의 정보는 레지스트리에 저장하며 다시 실행할 때 읽어와야 하는데 이 코드는 약간의 조작이 필요하므로 잠시 후에 작성하게 될 것이다. 서버 등록 및 수정은 다음 대화상자에서 한다.

최초 실행시에는 서버가 등록되어 있지 않으므로 이 대화상자는 비어 있을 것이다. 대화상자 프로시저는 다음과 같이 작성하였다.

 

BOOL CALLBACK DGFtpConfigProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     static int idx;

 

     switch(iMessage)

     {

     case WM_INITDIALOG:

          MoveToParentCenter(hDlg);

          idx=(int)lParam;

          if (idx == -1) {

               SetWindowText(hDlg," FTP 서버 등록");

              SetDlgItemText(hDlg,IDC_FTPPORT,"21");

              SetDlgItemText(hDlg,IDC_FTPUSER,"Anonymous");

          } else {

              SetWindowText(hDlg,"FTP 서버 정보 수정");

              SetDlgItemText(hDlg,IDC_FTPNAME,Option.arFtp[idx].Name);

              SetDlgItemText(hDlg,IDC_FTPADDR,Option.arFtp[idx].Addr);

              SetDlgItemText(hDlg,IDC_FTPUSER,Option.arFtp[idx].User);

              SetDlgItemText(hDlg,IDC_FTPPASS,Option.arFtp[idx].Pass);

              SetDlgItemInt(hDlg,IDC_FTPPORT,Option.arFtp[idx].Port,FALSE);

          }

          SendDlgItemMessage(hDlg,IDC_FTPNAME,EM_SETLIMITTEXT,31,0);

          SendDlgItemMessage(hDlg,IDC_FTPADDR,EM_SETLIMITTEXT,255,0);

          SendDlgItemMessage(hDlg,IDC_FTPUSER,EM_SETLIMITTEXT,31,0);

          SendDlgItemMessage(hDlg,IDC_FTPPASS,EM_SETLIMITTEXT,31,0);

          return TRUE;

     case WM_COMMAND:

          switch (LOWORD(wParam))

          {

          case IDOK:

              if (GetWindowTextLength(GetDlgItem(hDlg,IDC_FTPNAME)) == 0) {

                   MessageBox(hDlg,"접속명을 입력해 주십시오","알림",MB_OK);

                   return TRUE;

              }

              if (GetWindowTextLength(GetDlgItem(hDlg,IDC_FTPADDR)) == 0) {

                   MessageBox(hDlg,"서버의 주소를 입력해 주십시오","알림",MB_OK);

                   return TRUE;

              }

              if (GetWindowTextLength(GetDlgItem(hDlg,IDC_FTPUSER)) == 0) {

                   MessageBox(hDlg,"사용자 ID를 입력해 주십시오","알림",MB_OK);

                   return TRUE;

              }

              if (GetWindowTextLength(GetDlgItem(hDlg,IDC_FTPPASS)) == 0) {

                   MessageBox(hDlg,"비밀번호를 입력해 주십시오","알림",MB_OK);

                   return TRUE;

              }

              if (GetWindowTextLength(GetDlgItem(hDlg,IDC_FTPPORT)) == 0) {

                   MessageBox(hDlg,"접속 포트 번호를 입력해 주십시오","알림",MB_OK);

                   return TRUE;

              }

              if (idx==-1) {

                   for (idx=0;;idx++) {

                        if (lstrlen(Option.arFtp[idx].Name)==0)

                            break;

                   }

              }

              GetDlgItemText(hDlg,IDC_FTPNAME,Option.arFtp[idx].Name,32);

              GetDlgItemText(hDlg,IDC_FTPADDR,Option.arFtp[idx].Addr,256);

              GetDlgItemText(hDlg,IDC_FTPUSER,Option.arFtp[idx].User,32);

              GetDlgItemText(hDlg,IDC_FTPPASS,Option.arFtp[idx].Pass,32);

              Option.arFtp[idx].Port=GetDlgItemInt(hDlg,IDC_FTPPORT,NULL,FALSE);

              EndDialog(hDlg,IDOK);

              return TRUE;

          case IDCANCEL:

              EndDialog(hDlg,IDCANCEL);

              return TRUE;

          }

          return FALSE;

     }

     return FALSE;

}

 

WM_INITDIALOG lParam으로 편집할 서버의 첨자가 전달되며 -1일 경우 새로운 서버를 등록하라는 명령이다. 서버 편집일 경우는 각 에디트 컨트롤에 FTP 서버의 현재 정보를 보여주고 새 서버 등록일 경우는 포트 번호와 사용자 이름에 많이 쓰는 디폴트를 미리 입력했다. 사용자는 이 대화상자의 에디트 컨트롤에 접속할 서버의 정보를 입력 또는 수정한 후 확인 버튼을 클릭할 것이다.

서버 정보 수정일 경우 편집된 정보를 arFtp 배열에 다시 대입하며 새 서버 등록일 경우 arFtp배열의 빈 자리에 새로 정보를 작성한다. 사용자로부터 정보만 입력받는 것이 아니라 입력받은 정보를 저장하기까지 한다. 이 대화상자는 메뉴에서 직접 호출하지 않으며 FTP 열기 대화상자에 의해 호출된다.