Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Building Client/Server Applications with VC for Secure Private File Sharing

3.46/5 (9 votes)
9 Nov 20059 min read 2   2.8K  
How to build secure private file sharing client/server using a freeware SDK

Introduction

Most encrypted file sharing solutions today use digital certificates, which are designed for applications where there is a large user base not known in advance. However, if someone wants to avoid the cost and the hassle of obtaining and maintaining certificates, there is another possible solution around. In this article, a simple client/server solution is presented for secure private file sharing. The security is provided through symmetric encryption algorithm (AES 128) with shared secret keys. It could be more efficient and practical in many situations, when small groups are established.

Before We Start

Download and install the freeware edition of file sharing SDK, which is free for non-commercial use. The client/server components employ 128-bit AES encryption, without any need for SSL certificates. Instead, it uses a shared secret key, which creates an application-based virtual private network. In addition to standard FTP operations, such as upload, download, rename, delete, etc., the library also offers a number of advanced features, like opening of seekable streams on the server, remote searching for files and text, remote zip compression and decompression.

The Server

Image 1

Load the sample project FileServer.sln. The server component can be accessed via the control variable c_FileSrv. Let's review its main properties and methods.

Starting the Server

In order to start accepting connections, we have to go through the following steps:

  1. Setting the property ListenningPort to the number of the listening port. We take the value from the Settings form. The default one in our case is 2345.
  2. If we want to establish encrypted connections, we have to set the property SecurityMode to 2, and SecretKey to the value of the crypto key, specified on the settings form. If we don't need encryption, we must set SecurityMode to 0.
  3. If we want to use packet compression, we must set UseCompression to TRUE.
  4. Invoke the method Start().
C++
//Set the component properties
void CbsFileSrvDlg::SetProperties(void)
{
  c_FileSrv.put_ListeningPort(dlgSettings.m_Port);
  if ((dlgSettings.m_edKey) > "")
  {
    c_FileSrv.put_SecurityMode(2);  //shared secret key
    c_FileSrv.put_SecretKey(dlgSettings.m_edKey);
  }
  else
    c_FileSrv.put_SecurityMode(0);  //no encryption
  c_FileSrv.put_UseCompression(dlgSettings.m_chkCompress);
}


//Start the server
void CbsFileSrvDlg::OnBnClickedbtnstart()
{
  if (c_FileSrv.Start())
    LogMsg("Server started");
  else
    MessageBox("Cannot start the server!", "Error", MB_OK);
  UpdateStatus();
}

Stopping the Server

In order to stop accepting connections, we have to call the method Stop(). If we have some client currently connected, we should remove them with the method RemoveClient().

C++
//Stop the server
void CbsFileSrvDlg::OnBnClickedbtnstop()
{
  int i;
  LVITEM lvi;
  
  c_FileSrv.Stop();
  
  for (i=c_lvClients.GetItemCount()-1; i>= 0; i--)
  {
    lvi.mask =  LVIF_TEXT | LVIF_IMAGE;
    lvi.iItem = i;
    lvi.iSubItem = 0;
    if (c_lvClients.GetItem(&lvi))
      c_FileSrv.RemoveClient(int(lvi.iImage));
    c_lvClients.DeleteItem(i);
  }  
  
  UpdateStatus();
  LogMsg("Server stopped");
}

Adding a New Connection

When a new client is connected, the event OnNewClient is fired. At this point, we should take the client information (address/port) and store its handle somewhere for later on use.

C++
//A new connection is available
void CbsFileSrvDlg::OnNewClientBsfilesrvx(long aHandle)
{
  LVITEM lvi;
  CString Addr, Port, Tm;

  Addr = c_FileSrv.GetClientAddress(aHandle);
  Port.Format("%d", c_FileSrv.GetClientPort(aHandle));
  
  int i = c_lvClients.GetItemCount();

  lvi.mask =  LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  lvi.iItem = i;
  lvi.iSubItem = 0;
  lvi.lParam = aHandle;
  lvi.pszText = "Not signed in";
  c_lvClients.InsertItem(&lvi);

  c_lvClients.SetItemText(i, 1, Addr);
  c_lvClients.SetItemText(i, 2, Port);
  CTime tm = time(NULL);
  Tm = tm.Format("%m/%d/%Y %H:%M:%S");
  c_lvClients.SetItemText(i, 3, Tm);
  c_lvClients.SetItemText(i, 4, "Connected");
 
  UpdateStatus();
  LogMsg("New connection from " + Addr + ":" + Port);
}

Removing Disconnected Clients

When a connected client is disconnected, the event OnClientDisconnected is fired. At this point, we should release all data associated with this client.

C++
//A connection is broken
void CbsFileSrvDlg::OnClientDisconnectedBsfilesrvx(long aHandle)
{
  int i;
  LVITEM lvi;
  CString Name, Addr, Port;
  
  i = IndexFromHandle(aHandle);
  if (i < 0) return;
  
  lvi.mask =  LVIF_TEXT;
  lvi.iItem = i;
  lvi.iSubItem = 0;
  c_lvClients.GetItem(&lvi);

  Name = c_lvClients.GetItemText(i, 0);
  Addr = c_lvClients.GetItemText(i, 1);
  Name = c_lvClients.GetItemText(i, 2);

  LogMsg("Disconnected " + Name + " " + Addr + ":" + Port);
  c_FileSrv.RemoveClient(int(lvi.iImage));
  c_lvClients.DeleteItem(i);
  UpdateStatus();
}

Providing the User Password

When a connection is established, the client immediately sends a sign-in request. In our simple implementation, we accept only the default users (Guest). That's why in the handler of the event OnNeedPassword, the asked username should be always empty, and we always pass an empty string for the password value.

C++
//A request for a password
void CbsFileSrvDlg::OnNeedPasswordBsfilesrvx(long aHandle, 
            LPCTSTR aUsername, BOOL* aOkay, BSTR* aPassword)
{
  if (aUsername == "")
  {
    //this is the user Guest
    *aOkay = true;
  }
  else
  {
    //Unknown user, we accept only guests
    *aOkay = false;
  }
}

Signing In

After checking the password, the server component fires the event OnSignin. Since that moment, the client can perform various file operations.

C++
//A user is signed-in
void CbsFileSrvDlg::OnSigninBsfilesrvx(long aHandle, long aCode)
{
  int i;
  CString Name, S;
  
  i = IndexFromHandle(aHandle);
  if (i < 0) return;

  Name = GetUsername(aHandle);
  c_lvClients.SetItemText(i, 0, Name);
  
  S.Format("%s %s:%d", Name, c_FileSrv.GetClientAddress(aHandle), 
                                 c_FileSrv.GetClientPort(aHandle));
  if (aCode == 0)
    S = S + " signed in successfully";
  else
    S = S + " failed sign-in";
  LogMsg(S);
}

Listing the Folder Contents

The first thing which the client does after the signing in, is to get the (main) folder content. The handler of the event OnNeedListFolder is responsible to grant or refuse permission for the operation. There are two input and two output parameters:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the folder of interest
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to list the folder contents
void CbsFileSrvDlg::OnNeedListFolderBsfilesrvx(long aHandle, 
                          LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + ": list folder " + PreSlash(aPath));
}

The end of listing operation is signaled with the event OnListFolderDone.

Handling of "Create Folder" Requests

When a "create folder" request is received from the client, the event OnNeedNewFolder is fired. The handler must grant or refuse permission for the operation. The parameters are:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the file to be zipped
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to zip
void CbsFileSrvDlg::OnNeedZipBsfilesrvx(long aHandle, 
                 LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + 
            ": starting zip operaton" + PreSlash(aPath));
}

The event OnZipDone informs about the end of the operation.

C++
//The zip operation is completed
void CbsFileSrvDlg::OnZipDoneBsfilesrvx(long aHandle, long aCode)
{
  LogMsg(GetUsername(aHandle) + ": finishing zip operation");
}

Handling of "Rename File" Requests

When a "rename file" request is received from the client, the event OnNeedRename is fired. The handler must grant or refuse permission for the operation. The parameters are:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the file to be renamed
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to rename a file
void CbsFileSrvDlg::OnNeedRenameFileBsfilesrvx(long aHandle, 
                            LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + ": rename file " + PreSlash(aPath));
}

Handling of "Delete File" Requests

When a "delete file" request is received from the client, the event OnNeedDeleteFile is fired. The handler must grant or refuse permission for the operation. The parameters are:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the file to be deleted
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to delete a file
void CbsFileSrvDlg::OnNeedDeleteFileBsfilesrvx(long aHandle, 
                          LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + ": delete file " + PreSlash(aPath));
}

Handling of Download Requests

When a download request is received from the client, the event OnNeedDownload is fired. The handler must grant or refuse permission for the operation. The parameters are:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the file to be downloaded
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to download
void CbsFileSrvDlg::OnNeedDownloadBsfilesrvx(long aHandle, 
                      LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + 
                    ": starting download " + PreSlash(aPath));
}

The event OnDownloadDone informs about the end of the operation.

C++
//The download operation is completed
void CbsFileSrvDlg::OnDownloadDoneBsfilesrvx(long aHandle, 
                                                 long aCode)
{
  LogMsg(GetUsername(aHandle) + ": finishing download");
}

Handling of Upload Requests

When an upload request is received from the client, the event OnNeedUpload is fired. The handler must grant or refuse permission for the operation. The parameters are:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the file of interest
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to upload
void CbsFileSrvDlg::OnNeedUploadBsfilesrvx(long aHandle, 
                        LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + ": starting upload " + 
                                          PreSlash(aPath));
}

The event OnUploadDone informs about the end of the operation.

C++
//The upload operation is completed
void CbsFileSrvDlg::OnUploadDoneBsfilesrvx(long aHandle, long aCode)
{
  LogMsg(GetUsername(aHandle) + ": finishing upload");
}

Handling of Zipping Requests

When a zipping request is received from the client, the event OnNeedZip is fired. The handler must grant or refuse permission for the operation. The parameters are:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the file to be zipped
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to zip
void CbsFileSrvDlg::OnNeedZipBsfilesrvx(long aHandle, 
                     LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + ": starting zip operaton" + 
                                             PreSlash(aPath));
}

The event OnZipDone informs about the end of the operation.

C++
//The zip operation is completed
void CbsFileSrvDlg::OnZipDoneBsfilesrvx(long aHandle, long aCode)
{
  LogMsg(GetUsername(aHandle) + ": finishing zip operation");
}

Handling of Unzipping Requests

When an unzipping request is received from the client, the event OnNeedUnzip is fired. The handler must grant or refuse permission for the operation. The parameters are:

  • aHandle - Handle of the client object firing the event
  • aPath - Relative pathname of the file to be unzipped
  • aOkay - Set this variable to true in order to permit the operation
  • aRoot - If the operation is permitted, write here the root folder assigned to this user
C++
//A request to unzip
void CbsFileSrvDlg::OnNeedUnzipBsfilesrvx(long aHandle, 
                      LPCTSTR aPath, BOOL* aOkay, BSTR* aRoot)
{
  CString Dir = GetAppDir();
  *aOkay = true;
  *aRoot = Dir.AllocSysString(); 
  LogMsg(GetUsername(aHandle) + ": starting unzip operaton" + 
                                                 PreSlash(aPath));
}

The event OnUnzipDone informs about the end of the operation

C++
//The unzip operation is completed
void CbsFileSrvDlg::OnUnzipDoneBsfilesrvx(long aHandle, long aCode)
{
  LogMsg(GetUsername(aHandle) + ": finishing unzip operation");
}

The Client

Image 2

Load the sample project FileClient.sln. The client component can be accessed via the control variable c_FileCln. Let's review its main properties and methods.

Connecting to the Server

In order to establish a new connection to the server, we have to go through the following steps:

  1. If we want encrypted connections, we have to set the property SecurityMode to 2, and SecretKey to the value of the crypto key, specified on the "Connect" form. If we don't need encryption, we just set SecurityMode to 0.
  2. If we want to use packet compression, we must set the property UseCompression to TRUE.
  3. Invoking the method Connect(Addr, Port), passing as parameters the IP address/Domain name and the listening port of the server.
C++
//Initiate a connection request
void CbsFileClnDlg::OnBnClickedbtnconnect()
{
  CString Msg;
  if (dlgConnect.DoModal() != IDOK) return;

  if (dlgConnect.m_edKey > "")
  {
    c_FileCln.put_SecurityMode(2);  //shared secret key
    c_FileCln.put_SecretKey(dlgConnect.m_edKey);
  }
  else
    c_FileCln.put_SecurityMode(0);  //no encryption
    
  c_FileCln.put_UseCompression(dlgConnect.m_chkCompress);

  if (!c_FileCln.Connect(dlgConnect.m_edHost, dlgConnect.m_edPort))
  {
    Msg.Format("Error Code: %d", c_FileCln.get_LastError());
    AfxMessageBox(Msg);
  }
    
  UpdateStatus();
}

Disconnecting from the Server

In order to end a connection to the server, we have to call the method Disconnect().

C++
//Disconnect from the server
void CbsFileClnDlg::OnBnClickedbtndisconnect()
{
  if (!c_FileCln.get_Connected()) return;
  c_FileCln.Disconnect();
  OnDisconnectedBsfileclnx();
}

Listing Folder Contents

The method ListFolder() sends a request for folder contents:

C++
//Request the folder contents
void CbsFileClnDlg::ListFolder(void)
{
  CString S;
  if (m_NowList) return;
  c_stFolder.GetWindowText(S);  
  if (c_FileCln.ListFolder(S))
  {
    m_NowList = true;
    c_lvFiles.DeleteAllItems();
    c_btnList.EnableWindow(false);
    if (S > "\\")
      OnHaveListItemBsfileclnx("..", true, 0, 0, 0, 0);
  }
  else
    CheckError(c_FileCln.get_LastError());
}

The event OnHaveListItem is fired for each available folder item.

C++
//A list item is available
void CbsFileClnDlg::OnHaveListItemBsfileclnx(LPCTSTR aName, 
    BOOL aFolder, long aSizeLo, long aSizeHi, long aTimeLo, long aTimeHi)
{
  LVITEM lvi;
  CString Sz, Tm;
  FILETIME ft;

  int i = c_lvFiles.GetItemCount();

  lvi.mask =  LVIF_TEXT | LVIF_IMAGE;
  lvi.iItem = i;
  lvi.iSubItem = 0;
  if (aFolder)
  {
    lvi.iImage = 0;
    Sz = "";
  }  
  else
  {
    lvi.iImage = 1;
    Sz.Format("%d", aSizeLo);
  }
  
  lvi.pszText = (LPTSTR)(LPCTSTR) aName;
  c_lvFiles.InsertItem(&lvi);

  
  if (aName == "..")
    Tm = "";
  else
  {
    ft.dwLowDateTime = aTimeLo;
    ft.dwHighDateTime = aTimeHi;
    CTime tm(ft, -1);
    Tm = tm.Format("%m/%d/%Y %H:%M:%S");
  }
  c_lvFiles.SetItemText(i, 1, Sz);
  c_lvFiles.SetItemText(i, 2, Tm);
}

The end of the list is signaled with the event OnListFolderDone().

C++
//End of the list
void CbsFileClnDlg::OnListFolderDoneBsfileclnx(long aCode)
{
  m_NowList = false;
  CheckError (aCode);
  UpdateButtons();
}

Creating a New Folder

The method CreateFolder() sends a request to create a new folder.

C++
//Request a new folder
void CbsFileClnDlg::OnBnClickedbtnnew()
{
  CString Nm, Fol;
  if (!GetInputText(Nm, "Create folder", "Folder name:")) return;
  c_stFolder.GetWindowText(Fol);
  Nm = AddSlash(Fol) + Nm;
  if (c_FileCln.CreateFolder(Nm))
    m_NowCreateFolder = true;
  else
    CheckError(c_FileCln.get_LastError());
  UpdateButtons();
}

The event OnCreateFolderDone() is fired when the folder is created.

C++
//A new folder is created
void CbsFileClnDlg::OnCreateFolderDoneBsfileclnx(long aCode)
{
  m_NowCreateFolder = false;
  CheckError(aCode);
  UpdateButtons();
  if (aCode > 0) return;
  ListFolder();
}

Renaming a File

The method RenameFile() sends a request to rename a file on the server.

C++
//Send rename request
void CbsFileClnDlg::OnBnClickedbtnrename()
{
  CString Nm, Fol;
  if (c_lvFiles.GetSelectedCount() == 0) return;

  if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx) 
  {
    //folder
    if (!GetInputText(Nm, "Rename folder", "New name:")) return;
    Nm = AddSlash(GetCurFol()) + Nm;
    
    if (c_FileCln.RenameFolder(AddSlash(GetCurFol()) + 
              c_lvFiles.GetItemText(GetSelectedIndex(), 0), Nm))
      m_NowRenameFolder = true;
    else
      CheckError(c_FileCln.get_LastError());
  }
  else
  {
    //file
    if (!GetInputText(Nm, "Rename file", "New name:")) return;
    Nm = AddSlash(GetCurFol()) + Nm;
    
    if (c_FileCln.RenameFile(AddSlash(GetCurFol()) + 
            c_lvFiles.GetItemText(GetSelectedIndex(), 0), Nm))
      m_NowRenameFile = true;
    else
      CheckError(c_FileCln.get_LastError());
  }
  UpdateButtons();
}

The event OnRenameFileDone() is fired when the confirmation is received.

C++
//A file is renamed
void CbsFileClnDlg::OnRenameFileDoneBsfileclnx(long aCode)
{
  m_NowRenameFile = false;
  CheckError(aCode);
  UpdateButtons();
  if (aCode > 0) return;
  ListFolder();
}

Deleting a File

The method DeleteFile() sends a request to delete a file.

C++
//Send delete request
void CbsFileClnDlg::OnBnClickedbtndelete()
{
  CString Nm, Fol;
  if (c_lvFiles.GetSelectedCount() == 0) return;
  if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx) 
  {
    //folder
    if (c_FileCln.DeleteFolder(AddSlash(GetCurFol()) + 
             c_lvFiles.GetItemText(GetSelectedIndex(), 0)))
      m_NowDeleteFolder = true;
    else
      CheckError(c_FileCln.get_LastError());
  }
  else
  {
    //file
    if (c_FileCln.DeleteFile(AddSlash(GetCurFol()) + 
             c_lvFiles.GetItemText(GetSelectedIndex(), 0)))
      m_NowDeleteFile = true;
    else
      CheckError(c_FileCln.get_LastError());
  }
  UpdateButtons();
}

The event OnDeleteFileDone() is fired when the confirmation is received.

C++
//A file is deleted
void CbsFileClnDlg::OnDeleteFileDoneBsfileclnx(long aCode)
{
  m_NowDeleteFile = false;
  CheckError(aCode);
  UpdateButtons();
  if (aCode > 0) return;
  ListFolder();
}

Downloading a File

The method Download() sends a request to download a file.

C++
//Send a download request
void CbsFileClnDlg::GoDownload(void)
{
  CString Nm, Fol;

  if (c_lvFiles.GetSelectedCount() == 0) return;
  if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx) return;
  Nm = c_lvFiles.GetItemText(GetSelectedIndex(), 0);

  CFileDialog FileDlg(FALSE, "", Nm, 0, NULL);
  if( FileDlg.DoModal() != IDOK ) return;
  m_DnldFile = Nm;

  if (c_FileCln.Download(AddSlash(GetCurFol()) + Nm, 
                        ExtractFilePath(FileDlg.GetPathName())))
  {
    m_NowDownload = true;
    c_stDownload.SetWindowText(m_DnldFile + ": handshaking");
  }
  else
    CheckError (c_FileCln.get_LastError());
  UpdateStatus();
}

The event OnDownloadProgress() informs about the download progress.

C++
//Download progress info
void CbsFileClnDlg::OnDownloadProgressBsfileclnx(long aCountLo, 
                        long aCountHi, long aSizeLo, long aSizeHi)
{
  CString S;
  S.Format("%s - %d/%d", m_DnldFile, aCountLo, aSizeLo);
  c_stDownload.SetWindowText(S);
}

The event OnDownloadDone() is fired when the download is completed.

C++
//The download operation is completed
void CbsFileClnDlg::OnDownloadDoneBsfileclnx(long aCode)
{
  if (aCode == 0)
    c_stDownload.SetWindowText(m_DnldFile + " - Done.");
  else
    c_stDownload.SetWindowText(m_DnldFile + 
                     " - Aborted: " + ErrorText(aCode));
  m_NowDownload = false;
  UpdateButtons();
}

Uploading a File

The method Upload() sends a request to upload a file.

C++
//Send an upload request
void CbsFileClnDlg::OnBnClickedbtnupload()
{
  CFileDialog FileDlg(TRUE, "", NULL, 0, NULL);
  if( FileDlg.DoModal() != IDOK ) return;
  m_UpldFile = FileDlg.GetFileName();

  if (c_FileCln.Upload(FileDlg.GetPathName(), GetCurFol()))
  {
    m_NowUpload = true;
    m_UpldFol = GetCurFol();
    c_stUpload.SetWindowText(m_UpldFile + ": handshaking");
  }
  else
    CheckError (c_FileCln.get_LastError());
  
  UpdateStatus();
}

The event OnUploadProgress() provides information about the upload progress.

C++
//Upload progress info
void CbsFileClnDlg::OnUploadProgressBsfileclnx(long aCountLo, 
                     long aCountHi, long aSizeLo, long aSizeHi)
{
  CString S;
  S.Format("%s - %d/%d", m_UpldFile, aCountLo, aSizeLo);
  c_stUpload.SetWindowText(S);
}

The event OnUploadDone() is fired when the upload operation is completed.

C++
//The upload is completed
void CbsFileClnDlg::OnUploadDoneBsfileclnx(long aCode)
{
  if (aCode == 0)
    c_stUpload.SetWindowText(m_UpldFile + " - Done.");
  else
    c_stUpload.SetWindowText(m_UpldFile + " - Aborted: " + 
                                          ErrorText(aCode));
  m_NowUpload = false;
  UpdateButtons();
  if (m_UpldFol == GetCurFol())
    ListFolder();
}

Zipping

The Zip method starts a new compression operation.

VB
'Start the zip operation
Private Sub btnZip_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnZip.Click

  Dim Nm As String

  'ask the user for the zip file name
  Nm = InputBox("Zip file name", "Zipping")
  If Nm = "" Then Exit Sub 'canceled by the user

  If ExtractFileExt(Nm) = "" Then
    Nm = Nm + ".zip"
  End If

  If FileCln.Zip(AddSlash(txtFolder.Text) + Nm, _
                     AddSlash(txtFolder.Text) + _
                     lvFiles.SelectedItems(0).Text) Then
    'the request is accepted
    NowZip = True                   'raise the flag
    ZipFile = Nm                    'remember the zip file
    ZipFol = txtFolder.Text         'remember the current folder
    txtZip.Text = "Handshaking..."
  Else
    CheckError(FileCln.LastError)   'the request is rejected
  End If
  UpdateStatus()
End Sub

We prompt the user for the zip file name, and if it's not empty, we call the Zip method with two parameters. The first one is the relative pathname of the zip archive, the second one is the relative pathname of the file to be compressed. If the request is successful, we remember the current folder and the zip file name. Otherwise, we show an error message.

Showing the Progress Information

The event OnZipProgress event provides progress information about the zip operation.

C++
//Progress information is available for the zip operation
void CbsFileClnDlg::OnZipProgressBsfileclnx(long aFileCount, 
         long aFileTotal, long aByteCountLo, long aByteCountHi, 
         long aByteTotalLo, long aByteTotalHi)
{
  CString S;

  //Show the current byte count and the 
  //total amount of bytes (low 32 bits)
  S.Format("%s - %d/%d", m_ZipFile, aByteCountLo, aByteTotalLo);
  c_stZip.SetWindowText(S);
}

We display two counters (low 32 bits only) - the number of bytes compressed so far and the total number of requested bytes.

OnZipDone

The event OnZipDone reports the result of the zip operation.

C++
//The zip operation is completed
void CbsFileClnDlg::OnZipDoneBsfileclnx(long aCode)
{
  if (aCode == 0)
  {
    c_stZip.SetWindowText(m_ZipFile + " - Done."); //success
  }
  else
    c_stZip.SetWindowText(m_ZipFile + 
       " - Aborted: " + ErrorText(aCode));  //there is an error, 
                                            // describe it
  m_NowZip = false;

  if (m_ZipFol == GetCurFol())
    ListFolder();    //Refresh the folder

  UpdateButtons();
}

We display the completion status of the zip operation, and if the current folder is affected, we update the file list.

Unzip

The Unzip method starts a new decompression operation.

C++
//Start the unzip operation
void CbsFileClnDlg::OnBnClickedbtnunzip()
{
  CString Arc, Nm, Fol;

  if (c_lvFiles.GetSelectedCount() == 0) return;
  if (GetImageIndex(GetSelectedIndex()) == FolderImgIdx) return;
  Arc = c_lvFiles.GetItemText(GetSelectedIndex(), 0);
  
  if (ExtractFileExt(Arc) != ".zip") return;  //not a zip file

  //Ask the user for the extraction folder
  if (!GetInputText(Fol, "Unzipping", 
       "Extract to folder (leave blank for current):")) return;
    

  if (c_FileCln.Unzip(AddSlash(GetCurFol()) + Arc, Fol))
  {
    //the request is accepted
    m_NowUnzip = true;         //raise the flag   
    m_UnzipFile = Arc;         //remember the unzip file name
    m_UnzipFol = GetCurFol();  //remember the current folder
    c_stUnzip.SetWindowText(m_UnzipFile + ": handshaking");
  }
  else
    CheckError (c_FileCln.get_LastError());

  UpdateStatus();
}

We prompt the user for the destination folder, where the extracted file will be placed. Then we call the Unzip method with two parameters. The first one is the relative pathname of the zip archive, the second one is the relative pathname of the destination folder. If the request is successful, we remember the current folder and the zip file name. Otherwise, we show an error message.

OnUnzipProgress

OnUnzipProgress event provides progress information about the unzip operation.

C++
//Progress information is available for the zip operation
void CbsFileClnDlg::OnUnzipProgressBsfileclnx(long aFileCount, 
            long aFileTotal, long aByteCountLo, long aByteCountHi, 
            long aByteTotalLo, long aByteTotalHi)
{
  CString S;

  //Show the current byte count and the 
  //total amount of bytes (low 32 bits)
  S.Format("%s - %d/%d", m_ZipFile, aByteCountLo, aByteTotalLo);
  c_stUnzip.SetWindowText(S);
}

We display two counters (low 32 bits only) - the number of bytes decompressed so far, and the total number of bytes in the archive.

OnUnzipDone

OnUnzipDone event reports the result of the unzip operation.

C++
//The zip operation is completed
void CbsFileClnDlg::OnUnzipDoneBsfileclnx(long aCode)
{
  if (aCode == 0)
  {
    c_stUnzip.SetWindowText(m_UnzipFile + " - Done."); //success
  }
  else
    c_stUnzip.SetWindowText(m_UnzipFile + " - Aborted: " + 
            ErrorText(aCode));  //there is an error, describe it
  m_NowUnzip = false;

  if (m_UnzipFol == GetCurFol())
    ListFolder();    //Refresh the folder

  UpdateButtons();
}

We display the completion status of the unzip operation, and if the current folder is affected, we update the file list.

The Bottom Line

Nowadays, the Internet is insecure for a variety of reasons. Exchanging sensitive information could be a problem in many situations. In this article, we have presented one simple and affordable solution for secure private file sharing without the need for certificates.

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.