home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / winfe / cxsave.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  40.9 KB  |  1,484 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. // cxsave.cpp : implementation file
  20. //
  21.  
  22. #include "stdafx.h"
  23. #include "msgcom.h"
  24. #include "cxsave.h"
  25. #include "extgen.h"
  26. #include "intl_csi.h"
  27.  
  28. #ifdef _DEBUG
  29. #undef THIS_FILE
  30. static char BASED_CODE THIS_FILE[] = __FILE__;
  31. #endif
  32.  
  33. extern "C" int MK_DISK_FULL;        // defined in allxpstr.c
  34. extern char *FE_FindFileExt(char * path);
  35. extern void FE_LongNameToDosName(char* dest, char* source);
  36. extern void CheckLegalFileName(char* full_path);
  37.  
  38. /////////////////////////////////////////////////////////////////////////////
  39. // CSaveCX dialog
  40.  
  41. BOOL CSaveCX::SaveAnchorObject(const char *pAnchor, History_entry *pHist, int16 iCSID, CWnd *pParent, char *pFileName)
  42. {
  43.     //    Indirect constructor for serializing object.
  44.  
  45.     //    Allocate the dialog.
  46.     CSaveCX *pSaveCX = new CSaveCX(pAnchor, NULL, pParent);
  47.  
  48.     pSaveCX->m_iCSID = iCSID;
  49.  
  50.     // see if we've been given a file to load
  51.     if(pFileName)    {
  52.         pSaveCX->m_csFileName = pFileName;
  53.     }
  54.  
  55.     //    Assign over the history entry.
  56.     pSaveCX->m_pHist = pHist;
  57.  
  58.     //    See if we can create the dialog.
  59.     BOOL bCreate = pSaveCX->CanCreate();
  60.  
  61.     if(bCreate == TRUE)    {
  62.         //    Create the dialog.
  63.         pSaveCX->DoCreate();
  64.         return(TRUE);
  65.     }
  66.     else    {
  67.         //    No need to destroy a window, none created.
  68.         return(FALSE);
  69.     }
  70. }
  71.  
  72. // Kludge to avoid including cxsave.h in edview.cpp (freaks out Win16 compiler)
  73. BOOL wfe_SaveAnchorAsText(const char *pAnchor, History_entry *pHist,  CWnd *pParent, char *pFileName)
  74. {
  75.     return CSaveCX::SaveAnchorAsText(pAnchor, pHist, pParent, pFileName);
  76. }
  77.  
  78. // Similar to above, but special version for Composer
  79. //  to allow converting current HTML to text output
  80. BOOL CSaveCX::SaveAnchorAsText(const char *pAnchor, History_entry *pHist,  CWnd *pParent, char *pFileName)
  81. {
  82.     //    Allocate the dialog.
  83.     CSaveCX *pSaveCX = new CSaveCX(pAnchor, NULL, pParent);
  84.     if( !pSaveCX  || !pFileName )
  85.         return FALSE;
  86.  
  87.     // see if we've been given a file to load
  88.     pSaveCX->m_csFileName = pFileName;
  89.  
  90.     // We already know to save as Text, so set this here
  91.     // (CanCreate will not prompt for filename - we already selected it)
  92.     pSaveCX->m_iFileType = TXT;
  93.  
  94.     //    Assign over the history entry.
  95.     pSaveCX->m_pHist = pHist;
  96.  
  97.     //    See if we can create the dialog.
  98.     BOOL bCreate = pSaveCX->CanCreate();
  99.  
  100.     if(bCreate == TRUE)    {
  101.         //    Create the dialog.
  102.         pSaveCX->DoCreate();
  103.         return(TRUE);
  104.     }
  105.     else    {
  106.         //    No need to destroy a window, none created.
  107.         return(FALSE);
  108.     }
  109. }
  110.  
  111. NET_StreamClass *CSaveCX::SaveUrlObject(URL_Struct *pUrl, CWnd *pParent, char * pFileName)    
  112. {
  113.     //    Indirect constructor for serializing object.
  114.  
  115.     //    See if it's safe to convert the URL to this context.
  116.     if(NET_IsSafeForNewContext(pUrl) == FALSE)    {
  117.         return(NULL);
  118.     }
  119.   
  120.  
  121.     //    Allocate the dialog.
  122.     CSaveCX *pSaveCX = new CSaveCX(pUrl->address, NULL, pParent);
  123.  
  124.     // see if we've been given a file to load
  125.     if(pFileName)    {
  126.         pSaveCX->m_csFileName = pFileName;
  127.     }
  128.  
  129.     //    See if it's OK to further create the dialog.
  130.     pSaveCX->SetUrl(pUrl);
  131.     if(pSaveCX->CanCreate() == FALSE) {
  132.         return(NULL);
  133.     }
  134.  
  135.     //    Transfer the URL to the new context.
  136.     //    This ties together the dialog and the URL.
  137.     if(0 != NET_SetNewContext(pUrl, pSaveCX->GetContext(), CFE_GetUrlExitRoutine))    {
  138.         //    Couldn't transfer.
  139.         ASSERT(0);
  140.         pSaveCX->DestroyContext();
  141.         return(NULL);
  142.     }
  143.  
  144.     //    Manually bind the stream that will handle the download of
  145.     //        the URL, and return that stream.
  146.     //    This ties together the stream and dialog.
  147.     NET_StreamClass *pRetval = ContextSaveStream(FO_SAVE_AS, NULL, pUrl, pSaveCX->GetContext());
  148.     if(pRetval == NULL)    {
  149.         //    Couldn't create the stream.
  150.         pSaveCX->DestroyContext();
  151.         return(NULL);
  152.     }
  153.  
  154.     //    Create the dialog, the viewable portions.
  155.     pSaveCX->DoCreate();
  156.  
  157.     return(pRetval);
  158. }
  159.  
  160. NET_StreamClass *CSaveCX::ViewUrlObject(URL_Struct *pUrl, const char *pViewer, CWnd *pParent)    {
  161.     //    Indirect constructor for externally viewing object.
  162.  
  163.     //    See if it's safe to convert the URL to this context.
  164.     if(NET_IsSafeForNewContext(pUrl) == FALSE)    {
  165.         return(NULL);
  166.     }
  167.  
  168.     //    Allocate the dialog.
  169.     CSaveCX *pSaveCX = NULL;
  170.     if(pViewer != NULL && strlen(pViewer))  {
  171.         pSaveCX = new CSaveCX(pUrl->address, pViewer, pParent);
  172.     }
  173.     else    {
  174.         //  Shell execute style.
  175.         pSaveCX = new CSaveCX(pUrl->address, "ShellExecute", pParent);
  176.     }
  177.  
  178.     //    See if it's OK to further create the dialog.
  179.     if(pSaveCX->CanCreate(pUrl) == FALSE)    {
  180.         return(NULL);
  181.     }
  182.  
  183.     //    Transfer the URL to the new context.
  184.     //    This ties together the dialog and the URL.
  185.     if(0 != NET_SetNewContext(pUrl, pSaveCX->GetContext(), CFE_GetUrlExitRoutine))    {
  186.         //    Couldn't transfer.
  187.         ASSERT(0);
  188.         pSaveCX->DestroyContext();
  189.         return(NULL);
  190.     }
  191.     pSaveCX->SetUrl(pUrl);
  192.  
  193.     //    Manually bind the stream that will handle the download of
  194.     //        the URL, and return that stream.
  195.     //    This ties together the stream and dialog.
  196.     NET_StreamClass *pRetval = ContextSaveStream(FO_SAVE_AS, NULL, pUrl, pSaveCX->GetContext());
  197.     if(pRetval == NULL)    {
  198.         //    Couldn't create the stream.
  199.         pSaveCX->DestroyContext();
  200.         return(NULL);
  201.     }
  202.  
  203.     //    Create the dialog, the viewable portions.
  204.     pSaveCX->DoCreate();
  205.  
  206.     return(pRetval);
  207. }
  208.  
  209. NET_StreamClass *CSaveCX::OleStreamObject(NET_StreamClass *pOleStream, URL_Struct *pUrl, const char *pViewer, CWnd *pParent)    {
  210.     //    Indirect constructor for externally viewing object.
  211.  
  212.     //    See if it's safe to convert the URL to this context.
  213.     if(NET_IsSafeForNewContext(pUrl) == FALSE)    {
  214.         return(NULL);
  215.     }
  216.  
  217.     //    Allocate the dialog.
  218.     CSaveCX *pSaveCX = new CSaveCX(pUrl->address, pViewer, pParent);
  219.  
  220.     //    See if it's OK to further create the dialog.
  221.     if(pSaveCX->CanCreate() == FALSE)    {
  222.         return(NULL);
  223.     }
  224.  
  225.     //    Transfer the URL to the new context.
  226.     //    This ties together the dialog and the URL.
  227.     if(0 != NET_SetNewContext(pUrl, pSaveCX->GetContext(), CFE_GetUrlExitRoutine))    {
  228.         //    Couldn't transfer.
  229.         ASSERT(0);
  230.         return(NULL);
  231.     }
  232.     pSaveCX->SetUrl(pUrl);
  233.  
  234.     //    Speically set the secondary stream.
  235.     pSaveCX->SetSecondaryStream(pOleStream);
  236.  
  237.     //    Manually bind the stream that will handle the download of
  238.     //        the URL, and return that stream.
  239.     //    This ties together the stream and dialog.
  240.     NET_StreamClass *pRetval = ContextSaveStream(FO_SAVE_AS, NULL, pUrl, pSaveCX->GetContext());
  241.     if(pRetval == NULL)    {
  242.         //    Couldn't create the stream.
  243.         return(NULL);
  244.     }
  245.  
  246.     //    Create the dialog, the viewable portions.
  247.     pSaveCX->DoCreate();
  248.  
  249.     return(pRetval);
  250. }
  251.  
  252. CSaveCX::CSaveCX(const char *pAnchor, const char *pViewer, CWnd *pParent)
  253.     : CDialog(CSaveCX::IDD, pParent)
  254. {
  255.     iLastPercent = 0;
  256.     tLastBarTime = tLastTime = 0;
  257.     m_iFileType = ALL;
  258.  
  259.     m_pSink = NULL;
  260.  
  261.     //    We're not loading a URL yet.
  262.     m_pUrl = NULL;
  263.  
  264.     //    There's no history entry.
  265.     m_pHist = NULL;
  266.  
  267.     //    There's no secondary stream yet.
  268.     m_pSecondaryStream = NULL;
  269.  
  270.     //  We haven't been interrupted.
  271.     m_bInterrupted = FALSE;
  272.  
  273.     //  We haven't been abortioned.
  274.     m_bAborted = FALSE;
  275.  
  276.     //  We aren't saving to memory yet
  277.     m_bSavingToGlobal = FALSE;
  278.  
  279.     //  Set the context type.
  280.     m_cxType = Save;
  281.     GetContext()->type = MWContextSaveToDisk;
  282.  
  283.     //    No progress information as of yet.
  284.     m_lOldPercent = 0;
  285.  
  286.     // Do not know the character set yet
  287.     m_iCSID = 0;
  288.  
  289.     //  Set the anchor, and our viewer if possible.
  290.     //  We won't resolve any issues until later, when we can safely
  291.     //      fall out if the user chooses cancel in a file dialog or something.
  292.     if(pAnchor != NULL) {
  293.         m_csAnchor = pAnchor;
  294.     }
  295.     if(pViewer != NULL) {
  296.         m_csViewer = pViewer;
  297.     }
  298.  
  299.     //  Set our parent window.
  300.     m_pParent = pParent;
  301. #ifdef DEBUG
  302.     //  CWnd must not be a temporary object.
  303.     if(m_pParent) {
  304.         ASSERT(FromHandlePermanent(m_pParent->GetSafeHwnd()));
  305.     }
  306. #endif
  307.     //{{AFX_DATA_INIT(CSaveCX)
  308.     m_csAction = _T("");
  309.     m_csDestination = _T("");
  310.     m_csLocation = _T("");
  311.     m_csProgress = _T("");
  312.     m_csTimeLeft = _T("");
  313.     m_csPercentComplete = _T("");
  314.     //}}AFX_DATA_INIT
  315. }
  316.  
  317. // m_pUrl, if it exists will have been freed in CFE_GetUrlExitRoutine()
  318. CSaveCX::~CSaveCX() 
  319. {
  320.     //    Clean up any memory that's not handled before this.
  321.     //    We specifically allocated the save_as_name in the XP context.
  322.     //        for file saves only.
  323.     if(GetContext()->save_as_name != NULL)    {
  324.         free(GetContext()->save_as_name);
  325.         GetContext()->save_as_name = NULL;    //    In case someone else wants to free it, clear it.
  326.     }
  327.  
  328.     //  Clear back pointer.
  329.     m_pParent = NULL;
  330. }
  331.  
  332.  
  333. void CSaveCX::DoDataExchange(CDataExchange* pDX)
  334. {
  335.     CDialog::DoDataExchange(pDX);
  336.  
  337.     //{{AFX_DATA_MAP(CSaveCX)
  338.     DDX_Text(pDX, IDC_ACTION, m_csAction);
  339.     DDX_Text(pDX, IDC_DESTINATION, m_csDestination);
  340.     DDX_Text(pDX, IDC_LOCATION, m_csLocation);
  341.     DDX_Text(pDX, IDC_PROGRESS, m_csProgress);
  342.     DDX_Text(pDX, IDC_TIMELEFT, m_csTimeLeft);
  343.     DDX_Text(pDX, IDC_PERCENTCOMPLETE, m_csPercentComplete);
  344.     //}}AFX_DATA_MAP
  345. }
  346.  
  347. BEGIN_MESSAGE_MAP(CSaveCX, CDialog)
  348.     //{{AFX_MSG_MAP(CSaveCX)
  349.     ON_WM_PAINT()
  350.     ON_WM_QUERYDRAGICON()
  351.     ON_WM_SIZE()
  352.     ON_WM_SYSCOMMAND()
  353.     ON_WM_ERASEBKGND()
  354.     //}}AFX_MSG_MAP
  355. END_MESSAGE_MAP()
  356.  
  357.  
  358. /////////////////////////////////////////////////////////////////////////////
  359. // CSaveCX message handlers
  360.  
  361. void CSaveCX::OnCancel() 
  362. {
  363.     // TODO: Add extra cleanup here
  364.  
  365.     //  Mark that the user hit the cancel button, so that we can get
  366.     //      rid of the temp files when everything's kosher.
  367.     //  Do this before the interrupt, or other code may not know this fact.
  368.     m_bInterrupted = TRUE;
  369.  
  370.     //  Stop the load.
  371.     //    This variable should never be invalid while this message handler can be
  372.     //        called.
  373.     XP_InterruptContext(GetContext());
  374. }
  375.  
  376. HCURSOR CSaveCX::OnQueryDragIcon()    {
  377.     //    Return the icon that will show up when dragged.
  378.     HICON hIcon = theApp.LoadIcon(IDR_MAINFRAME);
  379.     ASSERT(hIcon);
  380.     return((HCURSOR)hIcon);
  381. }
  382.  
  383. BOOL CSaveCX::OnEraseBkgnd(CDC *pDC)    {
  384.     if(IsIconic() == TRUE)    {
  385.         return(TRUE);
  386.     }
  387.  
  388.     return(CDialog::OnEraseBkgnd(pDC));
  389. }
  390.  
  391. void CSaveCX::OnSysCommand(UINT nID, LPARAM lParam)    {
  392.     //    Don't maximize ourselves
  393.     if(nID == SC_MAXIMIZE)    {
  394.         return;
  395.     }
  396.  
  397.     CDialog::OnSysCommand(nID, lParam);
  398. }
  399.  
  400. void CSaveCX::OnSize(UINT nType, int cx, int cy) 
  401. {
  402.     //    Change any maximize request to a normal request.
  403.     if(nType == SIZE_MAXIMIZED)    {
  404.         nType = SIZE_RESTORED;
  405.     }
  406.  
  407.     CDialog::OnSize(nType, cx, cy);
  408. }
  409.  
  410. void CSaveCX::OnPaint() 
  411. {
  412.     TRY    {
  413.         CPaintDC dc(this);
  414.  
  415.         //    Check to see if we need to draw our icon.
  416.         if(IsIconic() != FALSE)    {
  417.             HICON hIcon = theApp.LoadIcon(IDR_MAINFRAME);
  418.             ASSERT(hIcon);
  419.             dc.DrawIcon(2, 2, hIcon);
  420.  
  421.             //    Also, make sure the window title is correct.
  422.             char aTitle[64];
  423.             //CLM: Removed hard-coded strings - be kind to International folks!
  424.             if(IsSaving() == TRUE)    {
  425.                 sprintf(aTitle, szLoadString(IDS_SAVE_SAVINGLOCATION), m_lOldPercent);
  426.             }
  427.             else    {
  428.                 sprintf(aTitle, szLoadString(IDS_SAVE_VIEWLOCATION), m_lOldPercent);
  429.             }
  430.             SetWindowText(aTitle);
  431.         }
  432.         else    {
  433.             //    Call the progress routine.
  434.             //    This will cause the painting of the progress bar to be
  435.             //        correct.
  436.             Progress(m_lOldPercent);
  437.  
  438.             //    Also, make sure the window title is correct.
  439.             if(IsSaving() == TRUE)    {
  440.                 SetWindowText(szLoadString(IDS_SAVING_LOCATION));
  441.             }
  442.             else    {
  443.                 SetWindowText(szLoadString(IDS_VIEWING_LOCATION));
  444.             }
  445.         }
  446.  
  447.     // Do not call CDialog::OnPaint() for painting messages
  448.     }
  449.     CATCH(CException, e)    {
  450.         //    Something went wrong.
  451.         return;
  452.     }
  453.     END_CATCH
  454. }
  455.  
  456. BOOL CSaveCX::Creator()
  457. {
  458.     BOOL bRetval = FALSE;
  459.     CWnd *pParent = m_pParent;
  460.     CWnd desktop;
  461.     
  462.     //  If no parent, use the desktop.
  463.     //  Should not be temporary CWnd returned from CWnd::GetDesktopWindow
  464.     //      as can theoretically be released if a URL completes and calls
  465.     //      the idle code during creation.
  466.     if(!pParent) {
  467.         desktop.Attach(::GetDesktopWindow());
  468.         pParent = &desktop;
  469.     }
  470.     
  471.     //  Do it.
  472.     bRetval = Create(CSaveCX::IDD, pParent);
  473.     
  474.     //  If we used the desktop, be sure to unattach before CWnd goes out
  475.     //      of scope.
  476.     if(pParent == &desktop) {
  477.         pParent->Detach();
  478.         pParent = NULL;
  479.     }
  480.     
  481.     return(bRetval);
  482. }
  483.  
  484. //    Determine if it's OK to create the dialog for the transfer.
  485. BOOL CSaveCX::CanCreate(URL_Struct* pUrl)    
  486. {
  487.     if(m_csAnchor.IsEmpty()) {
  488.         DestroyContext();
  489.         return(FALSE);
  490.     }
  491.  
  492.     //    Set some information for the dialog.
  493.     m_csLocation = m_csAnchor;
  494.     WFE_CondenseURL(m_csLocation, 40, FALSE);
  495.   
  496.     //  Are we asking them for a file name or stream? (not externally viewing)
  497.     if(IsSaving() == TRUE)    {
  498.  
  499.         // Don't ask for filename stuff if saving to stream.
  500.         if (!IsSavingToGlobal()) {
  501.             // Query for a filename if we don't have one already
  502.             if(m_csFileName.IsEmpty()) {
  503.  
  504. #ifdef MOZ_MAIL_NEWS
  505.                 char * pSuggested = MimeGuessURLContentName(GetContext(),m_csAnchor);
  506.                 if (!pSuggested)
  507. #else
  508.         char *            
  509. #endif /* MOZ_MAIL_NEWS */            
  510.                     pSuggested = fe_URLtoLocalName(m_csAnchor, pUrl ? pUrl->content_type : NULL);
  511.  
  512.                 char *pUserName = wfe_GetSaveFileName(NULL, szLoadString(IDS_SAVE_AS), pSuggested, &m_iFileType);
  513.  
  514.                 if(pSuggested)  {
  515.                     XP_FREE(pSuggested);
  516.                 }
  517.  
  518.                 if(pUserName == NULL) {
  519.                     DestroyContext();
  520.                     return(FALSE);
  521.                 }
  522.  
  523.                 m_csFileName = pUserName;
  524.                 XP_FREE(pUserName);
  525.             }
  526.  
  527.             //CLM: CHECK FOR SOURCE == DESTINATION
  528.             char * pSource = NULL;
  529.             XP_ConvertUrlToLocalFile(m_csAnchor, &pSource);
  530.             if( pSource && !m_csFileName.CompareNoCase(pSource) ){
  531.                 ::MessageBox(0, szLoadString( IDS_SOURCE_SAMEAS_DEST),
  532.                            szLoadString(IDS_SAVING_LOCATION), MB_OK | MB_ICONEXCLAMATION );
  533.                 DestroyContext();
  534.                 XP_FREE(pSource);
  535.                 return(FALSE);
  536.             }
  537.             if( pSource) XP_FREE(pSource);
  538.  
  539.             //    We need to copy the file name into the XP context's save_as_name,
  540.             //        so that other older code that depends on this will work (DDE).
  541.             if(GetContext()->save_as_name != NULL)    {
  542.                 ASSERT(FALSE);    //    Why isn't this NULL???
  543.                 free(GetContext()->save_as_name);
  544.                 GetContext()->save_as_name = NULL;
  545.             }
  546.             GetContext()->save_as_name = strdup((const char *)m_csFileName);
  547.         }
  548.  
  549.         //    Set some information in the dialog.
  550.         m_csAction.LoadString(IDS_SAVE_SAVING); //  = "Saving:";
  551.         m_csDestination = m_csFileName;
  552.         WFE_CondenseURL(m_csDestination, 40, FALSE);
  553.  
  554.         //    Create the viewable portions of the dialog.
  555.         Creator();
  556.  
  557.         //    Set its title.
  558.         SetWindowText(szLoadString(IDS_SAVING_LOCATION));
  559.  
  560.         //    Only ask for a URL if we don't already have one assigned
  561.         //        to us.
  562.         if(m_pUrl == NULL)    {
  563.             //  we've got a file name to save into.
  564.             //  the type of the file is set.
  565.             //  it would appear that everything is ready, ask for the URL.
  566.             //    We can only do this when saving.  Other methods must
  567.             //        manually set the URL.
  568.             if(m_pHist == NULL)    {
  569.                 m_pUrl = NET_CreateURLStruct(m_csAnchor, NET_DONT_RELOAD);
  570.  
  571.             } else {
  572.                 //    We are going to use the history entry of the other context
  573.                 //        instead.  In this manner, we can properly get the form
  574.                 //        data set for the load.
  575.                 m_pUrl = SHIST_CreateURLStructFromHistoryEntry(GetContext(), m_pHist);
  576.  
  577.                 // We need to make a copy of the form data, because we are in a
  578.                 // different context
  579.                 if (m_pUrl) {
  580.                     SHIST_SavedData    savedData;
  581.  
  582.                     memcpy(&savedData, &m_pUrl->savedData, sizeof(SHIST_SavedData));
  583.                     memset(&m_pUrl->savedData, 0, sizeof(SHIST_SavedData));
  584.                     LO_CloneFormData(&savedData, GetDocumentContext(), m_pUrl);
  585.                 }
  586.             }
  587.  
  588.             switch(m_iFileType) {
  589.                 case TXT:
  590.                     //  We need to ask the text front end to handle.
  591.                     TranslateText(m_pUrl, m_csFileName);
  592.                     break;
  593.  
  594.                 default:
  595.                     //  We handle ourselves.
  596.                     //  Will send through a particular format out stream.
  597.                     //    WE EXPECT NETLIB TO CALL OUR EXIT ROUTINE AND ALLCONNECTIONS COMPLETE
  598.                     //        IN ALL CASES.
  599.                     if(-1 == GetUrl(m_pUrl, FO_CACHE_AND_SAVE_AS))    {
  600.                         return(FALSE);
  601.                     }
  602.                     break;
  603.             }
  604.         }
  605.     }
  606.     else    {
  607.         //  We don't really care about the file type.
  608.         m_iFileType = ALL;
  609.         
  610.         //  Formulate a file name that will sink data from the net.
  611.         //  Security rist to let path information in externally provided
  612.         //      filename (content disposition, filename =)
  613.         char *pFormulateName;
  614.         if (pUrl && pUrl->content_name != NULL &&
  615.             strstr(pUrl->content_name, "../") == NULL &&
  616.             strstr(pUrl->content_name, "..\\") == NULL) {
  617.             pFormulateName = XP_STRDUP(pUrl->content_name);
  618.         }
  619.         else {
  620.             pFormulateName = fe_URLtoLocalName(m_csAnchor, pUrl ? pUrl->content_type : NULL);
  621.         }
  622.             
  623.         char *pLocalName = NULL;
  624.         if(((CNetscapeApp *)AfxGetApp())->m_pTempDir != NULL && pFormulateName != NULL) {
  625.             StrAllocCopy(pLocalName, ((CNetscapeApp *)AfxGetApp())->m_pTempDir);
  626.             StrAllocCat(pLocalName, "\\");
  627.             CheckLegalFileName(pFormulateName);
  628. #ifdef XP_WIN16
  629.             char dosName[13]; //8.3 with '\0' total 13 chars
  630.             FE_LongNameToDosName(dosName, pFormulateName);
  631.             StrAllocCat(pLocalName, dosName);
  632. #else 
  633.             StrAllocCat(pLocalName, pFormulateName);
  634. #endif
  635.  
  636.             //  If this file exists, then we must attempt another temp file.
  637.             if(-1 != _access(pLocalName, 0))    {
  638.                 int type = NET_URL_Type(pUrl->address);
  639.                 // bug 63751 for Mail/News attachment
  640.                 if ((type == MAILBOX_TYPE_URL) || (type == NEWS_TYPE_URL) || (type == IMAP_TYPE_URL))  
  641.     #ifdef XP_WIN16
  642.                     pLocalName = GetMailNewsTempFileName(pLocalName, dosName);
  643.     #else 
  644.                     pLocalName = GetMailNewsTempFileName(pLocalName);
  645.     #endif
  646.                 else    {
  647.                     //  Retain the extension.
  648.                     char aExt[_MAX_EXT];
  649.                     DWORD dwFlags = 0;
  650.                     size_t stExt = 0;
  651.                 
  652.                     aExt[0] = '\0';
  653.     #ifdef XP_WIN16
  654.                     dwFlags |= EXT_DOT_THREE;
  655.     #endif
  656.                     stExt = EXT_Invent(aExt, sizeof(aExt), dwFlags, pLocalName, pUrl ? pUrl->content_type : NULL);
  657.                 
  658.                     if(pLocalName) {
  659.                         XP_FREE(pLocalName);
  660.                         pLocalName = NULL;
  661.                     }
  662.                     pLocalName = WH_TempFileName(xpTemporary, "V", aExt);
  663.                 }
  664.             }
  665.         }
  666.         else    {
  667.             //  Retain the extension.
  668.             char aExt[_MAX_EXT];
  669.             DWORD dwFlags = 0;
  670.             size_t stExt = 0;
  671.             
  672.             aExt[0] = '\0';
  673. #ifdef XP_WIN16
  674.             dwFlags |= EXT_DOT_THREE;
  675. #endif
  676.             stExt = EXT_Invent(aExt, sizeof(aExt), dwFlags, m_csAnchor, pUrl ? pUrl->content_type : NULL);
  677.             
  678.             if(pLocalName) {
  679.                 XP_FREE(pLocalName);
  680.                 pLocalName = NULL;
  681.             }
  682.             pLocalName = WH_TempFileName(xpTemporary, "V", aExt);
  683.         }
  684.  
  685.         if(pFormulateName != NULL)  {
  686.             XP_FREE(pFormulateName);
  687.             pFormulateName = NULL;
  688.         }
  689.  
  690.         m_csFileName = pLocalName;
  691.  
  692.         //    Set some information for the dialog.
  693.         m_csAction.LoadString(IDS_SAVE_VIEWER); // "Viewer:"
  694.         m_csDestination = GetViewer();
  695.         WFE_CondenseURL(m_csDestination, 40, FALSE);
  696.  
  697.         //    Create the viewable portions of the dialog.
  698.         Creator();
  699.  
  700.         //    Set its title.
  701.         SetWindowText(szLoadString(IDS_VIEWING_LOCATION));
  702.  
  703.         //    We must wait for the URL to be assigned in manually.
  704.         //    DO NOT CREATE ONE.
  705.     }
  706.  
  707.     tFirstTime = theApp.GetTime();
  708.  
  709.     // made it
  710.     return(TRUE);
  711.  
  712. }
  713.  
  714. // for bug 63751
  715. // pFileName is intended for Win16 to check the prefix length of the 
  716. // filename since pTempPath is a full path.  Make sure you pass in a valid pointer
  717. //  If the prefix is shorter,
  718. // we append the number number after the prefix, otherwise we replace the 
  719. // last char for the number
  720. char* CSaveCX::GetMailNewsTempFileName(char* pTempPath, char *pFileName)    
  721. {
  722.     int nUniqueNo = 0;
  723.     char tempName[MAX_PATH + 3];
  724.     char extension[MAX_PATH];
  725.     char* pExt = NULL;
  726.  
  727.     strcpy(tempName, pTempPath);
  728.  
  729. #ifdef XP_WIN16
  730.     if (pFileName) {
  731.         pExt = FE_FindFileExt(pFileName);
  732.         *pExt = '\0';
  733.     }
  734. #endif
  735.  
  736.     pExt = FE_FindFileExt(tempName);
  737.     strcpy(extension, pExt);
  738.     *pExt = '\0';
  739.  
  740.     //  Get a name, If this file exists, then we attempt another temp file.
  741.     do {
  742.         char number[3];
  743.         int numLen;
  744.  
  745.         nUniqueNo += 1;
  746.         numLen = sprintf(number, "%d", nUniqueNo);
  747.  
  748. #ifdef XP_WIN16
  749.         *pExt = '\0';
  750.         if (strlen(pFileName) < (8 - numLen)) {
  751.             strcat(pExt, number);    //append if we can
  752.             *(pExt + numLen) = '\0';
  753.         }
  754.         else  {
  755.             strncpy((pExt - numLen), number, numLen);
  756.             *pExt = '\0';
  757.         }
  758. #else
  759.         strncpy(pExt, number, numLen);
  760.         *(pExt + numLen) = '\0';
  761. #endif
  762.         strcat(tempName, extension);
  763.  
  764.     } while (-1 != _access(tempName, 0)); 
  765.     
  766.     if(pTempPath != NULL)  {
  767.         XP_FREE(pTempPath);
  768.         pTempPath = NULL;
  769.     }
  770.     StrAllocCopy(pTempPath, tempName);
  771.  
  772.     return pTempPath;
  773. }
  774.  
  775. void CSaveCX::Progress(long lPercentage)    {
  776.     //    return if there's nothing to do.
  777.     if(lPercentage == 0)    {
  778.         return;
  779.     }
  780.  
  781.     // update the progress meter every second (at least)
  782.     int i;
  783.     for ( i = iLastPercent; i < lPercentage; i++ )
  784.         m_ProgressMeter.StepIt ( );
  785.     if ( lPercentage > iLastPercent )
  786.     {
  787.         time_t t;
  788.         t = theApp.GetTime();
  789.         if ( t - tLastBarTime )
  790.         {
  791.             tLastBarTime = t;
  792.             // print out the percent complete as well
  793.             char szPercent[8];
  794.             wsprintf(szPercent,"%d%%", (int) LOWORD(lPercentage));
  795.               SetDlgItemText(IDC_PERCENTCOMPLETE, szPercent );
  796.         }
  797.     }
  798.     iLastPercent = LOWORD(lPercentage);
  799. }
  800.  
  801. void CSaveCX::SetProgressBarPercent(MWContext *pContext, int32 lPercentage)    {
  802.     //    Make sure there's something to do here.
  803.     if(m_lOldPercent == lPercentage || lPercentage == 0)    {
  804.         return;
  805.     }
  806.  
  807.     m_lOldPercent = lPercentage;
  808.     
  809.     Progress(lPercentage);
  810.  
  811.     if(IsIconic() == TRUE)    {
  812.         char aTitle[64];
  813.         if(IsSaving() == TRUE)    {
  814.             sprintf(aTitle, szLoadString(IDS_SAVE_SAVINGLOCATION), lPercentage);
  815.         }
  816.         else    {
  817.             sprintf(aTitle, szLoadString(IDS_SAVE_VIEWLOCATION), lPercentage);
  818.         }
  819.         SetWindowText(aTitle);
  820.     }
  821. }
  822.  
  823. CWnd *CSaveCX::GetDialogOwner() const    {
  824.     //    Return this dialog as the owner of any other dialogs that
  825.     //        may be created in base classes.
  826.     //    No need to call the base.
  827.     return((CWnd *)this);
  828. }
  829.  
  830. void CSaveCX::GetUrlExitRoutine(URL_Struct *pUrl, int iStatus, MWContext *pContext)    {
  831.     //    Call the base.
  832.     CStubsCX::GetUrlExitRoutine(pUrl, iStatus, pContext);
  833.  
  834.     //    If the URL is changine context, then handle correctly by not freeing.
  835.     if(iStatus == MK_CHANGING_CONTEXT)    {
  836.         m_pUrl = NULL;
  837.     }
  838.  
  839.     //    Just destroy the window, object gets destroyed in all connections complete.
  840.     //    Fun's over.
  841.     DestroyWindow();
  842. }
  843.  
  844. void CSaveCX::TextTranslationExitRoutine(PrintSetup *pTextFE)    {
  845.     //    Call the base.
  846.     CStubsCX::TextTranslationExitRoutine(pTextFE);
  847.  
  848.     //    Destroy this object, object doesn't get destroyed in all connections complete.
  849.     //    Fun's over.
  850.     DestroyWindow();
  851.     DestroyContext();
  852. }
  853.  
  854. void CSaveCX::AllConnectionsComplete(MWContext *pContext)    {
  855.     //    Go ahead and delete this context.
  856.     //    Won't be doing anything else.
  857.     DestroyContext();
  858. }
  859.  
  860. void CSaveCX::Progress(MWContext *pContext, const char *pMessage)    {
  861.     //    Set our progress message.
  862.     if(pMessage && !strchr(pMessage,'%') && ::IsWindow(m_hWnd)) {
  863.         SetDlgItemText(IDC_PROGRESS, pMessage);
  864.     }
  865. }
  866.  
  867. void CSaveCX::GraphProgress(MWContext *pContext, URL_Struct *pURL, int32 lBytesReceived, int32 lBytesSinceLastTime, int32 lContentLength)    {
  868.     //    call the base.
  869.     CStubsCX::GraphProgress(pContext, pURL, lBytesReceived, lBytesSinceLastTime, lContentLength);
  870.  
  871.     // update the progress message after at least 1 second has passed
  872.     char szMessage[50];
  873.     unsigned long lKReceived = lBytesReceived / 1024;
  874.     time_t t, tdiff;
  875.     t = theApp.GetTime();
  876.     if ( t - tLastTime )
  877.     {
  878.         tLastTime = t;
  879.         tdiff = t - tFirstTime;
  880.         if ( !tdiff ) 
  881.             tdiff = 1;
  882.         unsigned long bytes_sec = lBytesReceived / tdiff;
  883.         if ( !bytes_sec )
  884.             bytes_sec = 1;
  885.         if ( lContentLength == 0 )
  886.             wsprintf ( szMessage, "%ldK of Unknown (at %ld.%ldK/sec)",
  887.                 (long)lKReceived, 
  888.                 (long)(bytes_sec / 1000L), 
  889.                 (long)( ( bytes_sec % 1000L ) / 100 ) );
  890.         else
  891.             wsprintf ( szMessage, "%ldK of %ldK (at %ld.%ldK/sec)",
  892.                 (long)lKReceived, 
  893.                 (long)lContentLength / 1024L, 
  894.                 (long)(bytes_sec / 1000L), 
  895.                 (long)( ( bytes_sec % 1000L ) / 100 ) );
  896.         CFE_Progress(pContext, szMessage );
  897.  
  898.         if ( lContentLength == 0 )
  899.         {
  900.             // do we know the content length?
  901.             if ( !lKReceived )
  902.                 SetDlgItemText ( IDC_TIMELEFT, szLoadString(IDS_UNKNOWN_TIMELEFT));
  903.         }
  904.         else
  905.         {
  906.             char szTime[10];
  907.             time_t tleft = ( lContentLength - lBytesReceived ) / bytes_sec;
  908.             wsprintf ( szTime, "%02ld:%02ld:%02ld", 
  909.                 (long)(tleft / 3600L),
  910.                 (long)(( tleft % 3600L ) / 60L),
  911.                 (long)(( tleft % 3600L ) % 60L ));
  912.             SetDlgItemText ( IDC_TIMELEFT, szTime );
  913.         }
  914.     }
  915.  
  916.     //    Draw our progress bar from this information.
  917.     CFE_SetProgressBarPercent( pContext, lContentLength != 0 ? lBytesReceived * 100 / lContentLength : 0 );
  918. }
  919.  
  920. extern "C"  {
  921.  
  922. NET_StreamClass *ContextSaveStream(int iFormatOut, void *pDataObj, URL_Struct *pUrl, MWContext *pContext)   {
  923.     ASSERT(iFormatOut & FO_SAVE_AS);
  924.  
  925.     /* settings to enable FTP and HTTP restart of interrupted downloads */
  926.     if(pUrl->server_can_do_byteranges || pUrl->server_can_do_restart)
  927.         pUrl->must_cache = TRUE;
  928.  
  929.     //    If we're saving from a context not meant to save, then intro the nasty hack.
  930.     if(ABSTRACTCX(pContext)->GetContextType() != Save)    {
  931.         NET_StreamClass *pStreamHack = CSaveCX::SaveUrlObject(pUrl, NULL, pContext->save_as_name);
  932.         //    steal the save as name (avoid always saving to the same file if this happens many times).
  933.         if(pContext->save_as_name)    {
  934.             free(pContext->save_as_name);
  935.             pContext->save_as_name = NULL;
  936.         }
  937.         return(pStreamHack);
  938.     }
  939.  
  940.     //  We're to save a stream to disk or a secondary stream.
  941.     //  First thing to do is find our CSaveCX object.
  942.     CSaveCX *pSaveCX = VOID2CX(pContext->fe.cx, CSaveCX);
  943.  
  944.     //  Create the stream which will do the actual work.
  945.     NET_StreamClass *pStream = NET_NewStream("Context save THIS buddy",
  946.         ContextSaveWrite,
  947.         ContextSaveComplete,
  948.         ContextSaveAbort,
  949.         ContextSaveReady,
  950.         CX2VOID(pSaveCX, CSaveCX),
  951.         pContext);
  952.  
  953.     if(pStream == NULL) {
  954.         ASSERT(0);
  955.         return(NULL);
  956.     }
  957.  
  958.     //    Don't create an output file if all we're doing is proxying to
  959.     //        another stream.
  960.     if(pSaveCX->GetSecondaryStream() == NULL)    {
  961.         //  All we need to do now is save the file, the name should already be
  962.         //      established inside the context class.
  963.         ASSERT(pSaveCX->GetFileName().IsEmpty() == FALSE);
  964.  
  965.         //  See if this is a text file.
  966.         //  Leave as shared readable for DDE apps looking into the file early.
  967.         int iOpenFlags = CStdioFile::modeCreate | CStdioFile::modeWrite;
  968.  #ifdef _WIN32
  969.          iOpenFlags |= CStdioFile::shareDenyWrite;
  970.  #endif
  971.  
  972.         ASSERT(pUrl->content_type != NULL);
  973.         CString csContentType = pUrl->content_type;
  974.         csContentType = csContentType.Left(5);
  975.         if(csContentType == "text/")    {
  976.             iOpenFlags |= CStdioFile::typeText;
  977.         }
  978.         else    {
  979.             iOpenFlags |= CStdioFile::typeBinary;
  980.         }
  981.  
  982.         //    See if the URL has a content length, and if so, see if the device
  983.         //        we'll be writing to has enough free space.
  984.         if(FEU_ConfirmFreeDiskSpace(pSaveCX->GetContext(), pSaveCX->GetFileName(), pUrl->content_length) == FALSE)    {
  985.             //    Not enough space to continue.
  986.             XP_FREE(pStream);
  987.             return(NULL);
  988.         }
  989.  
  990.         //  Open the file
  991.         CStdioFile *pSink;
  992.         TRY    {
  993.             pSink = new CStdioFile(pSaveCX->GetFileName(), iOpenFlags);
  994.         }
  995.         CATCH(CException, e)    {
  996.             CString    strMsg;
  997.  
  998.             strMsg.LoadString(IDS_NO_OPEN_WRITE);
  999.             FE_Alert(pSaveCX->GetContext(), (LPCSTR)strMsg);
  1000.             pSink = NULL;
  1001.         }
  1002.         END_CATCH
  1003.  
  1004.         if(pSink == NULL)   {
  1005.             ASSERT(0);
  1006.             XP_FREE(pStream);
  1007.             return(NULL);
  1008.         }
  1009.  
  1010.         //  Let the context know where the output is going.
  1011.         pSaveCX->SetSink(pSink);
  1012.     }
  1013.  
  1014.     //  We're done.
  1015.     return(pStream);
  1016. }
  1017.  
  1018. unsigned int ContextSaveReady(NET_StreamClass *stream)    {
  1019.     void *pDataObj=stream->data_object;
  1020.     //    Get our save context out of the data object.
  1021.     CSaveCX *pSaveCX = VOID2CX(pDataObj, CSaveCX);    
  1022.  
  1023.     //    See if we need to handle specially for a secondary stream.
  1024.     NET_StreamClass *pSecondary = pSaveCX->GetSecondaryStream();
  1025.     if(pSecondary == NULL)    {
  1026.         //    We should always be ready to write the maximum amount out to disk.
  1027.         return(MAX_WRITE_READY);
  1028.     }
  1029.     else    {
  1030.         return(pSecondary->is_write_ready(pSecondary));
  1031.     }
  1032. }
  1033.  
  1034. int ContextSaveWrite(NET_StreamClass *stream, const char *pWriteData, int32 iDataLength)    {
  1035.     void *pDataObj=stream->data_object;
  1036.     //    Get our save context out of the data object.
  1037.     CSaveCX *pSaveCX = VOID2CX(pDataObj, CSaveCX);
  1038.     CStdioFile *pSink = pSaveCX->GetSink();
  1039.     int iRetval = (UINT)iDataLength;    
  1040.  
  1041.     //    See if we need to handle specially for a secondary stream.
  1042.     NET_StreamClass *pSecondary = pSaveCX->GetSecondaryStream();
  1043.     if(pSecondary == NULL)    {    
  1044.         //    Write the data to disk.
  1045.         TRY    {
  1046.             pSink->Write((void *)pWriteData, (UINT)iDataLength);
  1047.         }
  1048.         CATCH(CException, e)    {
  1049.             //    Some type of error occurred.
  1050.             pSaveCX->Interrupt();
  1051.  
  1052.             CString    strMsg;
  1053.  
  1054.             strMsg.LoadString(IDS_CANT_WRITE);
  1055.             FE_Alert(pSaveCX->GetContext(), (LPCSTR)strMsg);
  1056.             iRetval = MK_DISK_FULL;
  1057.         }
  1058.         END_CATCH
  1059.     }
  1060.     else    {
  1061.         iRetval = pSecondary->put_block(pSecondary, pWriteData, iDataLength);
  1062.     }
  1063.  
  1064.     //    Return the amount written, or error.
  1065.     return(iRetval);
  1066. }
  1067.  
  1068. void ContextSaveComplete(NET_StreamClass *stream)    {
  1069.     void *pDataObj=stream->data_object;
  1070.     //    The save is done.  Close the file.
  1071.     CSaveCX *pSaveCX = VOID2CX(pDataObj, CSaveCX);    
  1072.  
  1073.     if(!pSaveCX->m_bAborted)
  1074.     {
  1075.         // get rid of the cache file since we don't need it for
  1076.         // ftp restarts anymore since it successfully download
  1077.         if(pSaveCX->m_pUrl)
  1078.             NET_RemoveURLFromCache(pSaveCX->m_pUrl);
  1079.     }
  1080.  
  1081.     //    See if we need to handle specially for a secondary stream.
  1082.     NET_StreamClass *pSecondary = pSaveCX->GetSecondaryStream();
  1083.     if(pSecondary == NULL)    {
  1084.         TRY    {
  1085.             delete(pSaveCX->GetSink());
  1086.         }
  1087.         CATCH(CException, e)    {
  1088.             //    Disk full or some other error condition for Close();
  1089.             //    Don't do anything.
  1090.             CString    strMsg;
  1091.  
  1092.             strMsg.LoadString(IDS_CANT_CLOSE);
  1093.             FE_Alert(pSaveCX->GetContext(), (LPCSTR)strMsg);
  1094.         }
  1095.         END_CATCH
  1096.         pSaveCX->ClearSink();
  1097.  
  1098.         //  If we've been interrupted, we're going to want to clean up the partial droppings
  1099.         //      on disk.
  1100.         if(pSaveCX->m_bInterrupted)  {
  1101.             TRY {
  1102.                 CFile::Remove(pSaveCX->GetFileName());
  1103.             }
  1104.             CATCH(CException, e)    {
  1105.                 //  Report it to the person wanting to cancel the operation.
  1106.                 //  Failure...
  1107.                 CString    strMsg;
  1108.  
  1109.                 strMsg.LoadString(IDS_CANT_CLEANUP);
  1110.                 FE_Alert(pSaveCX->GetContext(), (LPCSTR)strMsg);
  1111.             }
  1112.             END_CATCH
  1113.         }    
  1114.         //    If this were going to an external viewer, we'll want to remove the file on exit.
  1115.         else if(pSaveCX->IsViewing() == TRUE)    {
  1116.             FE_DeleteFileOnExit(pSaveCX->GetFileName(),  pSaveCX->GetAnchor());
  1117.  
  1118.             //    We'll also want to start that viewer now.
  1119.             //    It should have previously been verified as a viewer OK to spawn prior to switching
  1120.             //        to this context.
  1121.             FE_Progress(pSaveCX->GetContext(), szLoadString(IDS_SPAWNING_EXTERNAL_VIEWER));
  1122.             if(pSaveCX->IsShellExecute())   {
  1123.                 FEU_Execute(pSaveCX->GetContext(), pSaveCX->GetFileName(), NULL);
  1124.             }
  1125.             else    {
  1126. #ifdef XP_WIN32
  1127.                 // Pass an 8.3 filename to the helper app in case it doesn't understand long filenames
  1128.                 char    szShortFileName[_MAX_PATH];
  1129.  
  1130.                 VERIFY(GetShortPathName(pSaveCX->GetFileName(), szShortFileName, sizeof(szShortFileName)) > 0);
  1131.                 FEU_Execute(pSaveCX->GetContext(), pSaveCX->GetViewer(), szShortFileName);
  1132. #else
  1133.                 FEU_Execute(pSaveCX->GetContext(), pSaveCX->GetViewer(), pSaveCX->GetFileName());
  1134. #endif
  1135.             }
  1136.         }
  1137.     }
  1138.     else    {
  1139.         pSecondary->complete(pSecondary);
  1140.         XP_FREE(pSecondary);
  1141.         pSaveCX->ClearSecondary();
  1142.     }
  1143. }
  1144.  
  1145. void ContextSaveAbort(NET_StreamClass *stream, int iStatus)    {
  1146.     void *pDataObj=stream->data_object;
  1147.     //    The save is done.  Close the file.
  1148.     CSaveCX *pSaveCX = VOID2CX(pDataObj, CSaveCX);    
  1149.  
  1150.     pSaveCX->m_bAborted = TRUE;
  1151.  
  1152.     //    See if we need to handle specially for a secondary stream.
  1153.     NET_StreamClass *pSecondary = pSaveCX->GetSecondaryStream();
  1154.     if(pSecondary == NULL)    {
  1155.         //    The load was aborted.
  1156.         //    Handle as a normally compeleted stream.
  1157.         ContextSaveComplete(stream);
  1158.     }
  1159.     else    {
  1160.         pSecondary->abort(pSecondary, iStatus);
  1161.         XP_FREE(pSecondary);
  1162.         pSaveCX->ClearSecondary();
  1163.     }
  1164. }
  1165.  
  1166. };
  1167.  
  1168. BOOL CSaveCX::OnInitDialog() 
  1169. {
  1170.     CDialog::OnInitDialog();
  1171.  
  1172.     m_ProgressMeter.SubclassDlgItem( IDC_PROGRESSMETER, this );
  1173.  
  1174.     //    Set some information for the dialog.
  1175.     m_csLocation = m_csAnchor;
  1176.     CStatic * pStatic = (CStatic *)GetDlgItem(IDC_LOCATION);
  1177.     int iLocWidth = 40;
  1178.     if ( pStatic != NULL )
  1179.     {
  1180.         CRect rect;
  1181.         TEXTMETRIC tm;
  1182.         pStatic->GetClientRect( &rect );
  1183.         CDC * pdc =  pStatic->GetDC();
  1184.         pdc->GetTextMetrics ( &tm );
  1185.         iLocWidth = rect.Width() / tm.tmAveCharWidth;
  1186.         pStatic->ReleaseDC ( pdc );
  1187.     };
  1188.  
  1189.  
  1190.     WFE_CondenseURL(m_csLocation, iLocWidth, FALSE);
  1191.     SetDlgItemText (IDC_LOCATION, m_csLocation );
  1192.  
  1193.     WFE_CondenseURL(m_csDestination, iLocWidth, FALSE);
  1194.     SetDlgItemText (IDC_DESTINATION, m_csDestination );
  1195.  
  1196. #ifdef PMETER
  1197.     m_ProgressCtl.SetRange ( 0, 100 );
  1198.     m_ProgressCtl.SetStep ( 1 );
  1199. #endif
  1200.     
  1201.     return TRUE;  // return TRUE unless you set the focus to a control
  1202.                   // EXCEPTION: OCX Property Pages should return FALSE
  1203. }
  1204.  
  1205. CString CSaveCX::GetViewer() const
  1206. {
  1207.     CString csRetval = m_csViewer;
  1208.  
  1209.     if(IsShellExecute())  {
  1210.         char aViewer[_MAX_PATH];
  1211.         memset(aViewer, 0, sizeof(aViewer));
  1212.         
  1213.         BOOL bViewer = FEU_FindExecutable(GetFileName(), aViewer, FALSE);
  1214.         if(bViewer)  {
  1215.             csRetval = aViewer;
  1216.         }
  1217.     }
  1218.  
  1219.     return(csRetval);
  1220. }
  1221.  
  1222. /////////////////////////////////////////////////////////////////////
  1223. //
  1224. // CSaveAttachmentStream
  1225. //
  1226. // for attachment download for drag-and-drop
  1227. //
  1228.  
  1229. class CSaveAttachmentStream {
  1230. protected:
  1231.     HGLOBAL m_hGlobal;
  1232.     int32 m_nPosition;
  1233.     int32 m_nFileSize;
  1234.     int32 m_nBufferSize;
  1235.  
  1236.     BOOL *m_pbStatus;
  1237.     NET_StreamClass *m_pStream;
  1238.     
  1239.     int16 m_mail_csid;
  1240.     int16 m_win_csid;
  1241.     CCCDataObject m_converter;
  1242.     XP_Bool m_doConvert;
  1243.  
  1244. public:
  1245.     CSaveAttachmentStream(MWContext *pContext, HGLOBAL hGlobal, LPCTSTR lpszUrl, LPCTSTR lpszTitle, BOOL *pbStatus);
  1246.     ~CSaveAttachmentStream();
  1247.  
  1248.     NET_StreamClass *GetStream() const { return m_pStream; }
  1249.  
  1250.     // Stream Stuff
  1251.     int Write(const char *pWriteData, int32 iDataLength);
  1252.     void Complete();
  1253.     void Abort(int iStatus);
  1254.  
  1255.     // Global Stuff
  1256.     BOOL Grow(int32 nAdd);
  1257.     BOOL SetSize(int32 nSize);
  1258.     void SetupINTLConverter();
  1259. };
  1260.  
  1261. extern "C" {
  1262.  
  1263. unsigned int AttachmentSaveReady(NET_StreamClass *stream)
  1264. {    
  1265.     return MAX_WRITE_READY;
  1266. }
  1267.  
  1268. int AttachmentSaveWrite(NET_StreamClass *stream, const char *pWriteData, int32 iDataLength)
  1269. {
  1270.     void *pDataObj=stream->data_object;
  1271.     CSaveAttachmentStream *pSaveCX = (CSaveAttachmentStream *) pDataObj;    
  1272.     return pSaveCX->Write(pWriteData, iDataLength);
  1273. }
  1274.  
  1275. void AttachmentSaveComplete(NET_StreamClass *stream)
  1276. {
  1277.     void *pDataObj=stream->data_object;
  1278.     CSaveAttachmentStream *pSaveCX = (CSaveAttachmentStream *) pDataObj;    
  1279.     pSaveCX->Complete();
  1280. }
  1281.  
  1282. void AttachmentSaveAbort(NET_StreamClass *stream, int iStatus)
  1283. {
  1284.     void *pDataObj=stream->data_object;
  1285.     CSaveAttachmentStream *pSaveCX = (CSaveAttachmentStream *) pDataObj;    
  1286.     pSaveCX->Abort(iStatus);
  1287. }
  1288.  
  1289. }; // extern "C"
  1290.  
  1291. CSaveAttachmentStream::CSaveAttachmentStream(MWContext *pContext, HGLOBAL hGlobal, LPCTSTR lpszUrl, LPCTSTR lpszTitle, BOOL *pbStatus)
  1292. {
  1293.     m_hGlobal = hGlobal;
  1294.     m_nBufferSize = ::GlobalSize(m_hGlobal);
  1295.     m_nPosition = 0;
  1296.     m_nFileSize = 0;
  1297.  
  1298.     m_pStream = NET_NewStream("SaveAttachment",
  1299.         AttachmentSaveWrite,
  1300.         AttachmentSaveComplete,
  1301.         AttachmentSaveAbort,
  1302.         AttachmentSaveReady,
  1303.         this,
  1304.         pContext);
  1305.  
  1306.     m_pbStatus = pbStatus;
  1307.  
  1308.     if (m_pbStatus)
  1309.         *m_pbStatus = TRUE;
  1310.  
  1311.     m_mail_csid = 0;
  1312.     m_win_csid = 0;
  1313.     m_converter = NULL;
  1314.     m_doConvert = FALSE;
  1315. }
  1316.  
  1317. CSaveAttachmentStream::~CSaveAttachmentStream()
  1318. {
  1319.     if (m_converter)
  1320.     {
  1321.         INTL_DestroyCharCodeConverter(m_converter);
  1322.         m_converter = NULL;
  1323.     }
  1324. }
  1325.  
  1326. void CSaveAttachmentStream::SetupINTLConverter()
  1327. {
  1328.     INTL_CharSetInfo csi =
  1329.         LO_GetDocumentCharacterSetInfo(m_pStream->window_id);
  1330.     char *mime_charset = (csi == NULL) ? NULL : INTL_GetCSIMimeCharset(csi);
  1331.  
  1332.     if (!m_converter &&
  1333.         mime_charset && 
  1334.         *(mime_charset))
  1335.     {
  1336.         m_mail_csid = INTL_CharSetNameToID(mime_charset);
  1337.         m_win_csid = INTL_DocToWinCharSetID(m_mail_csid);
  1338.         m_converter = INTL_CreateCharCodeConverter();
  1339.         if (m_converter)
  1340.             m_doConvert = INTL_GetCharCodeConverter(m_mail_csid, m_win_csid, m_converter);
  1341.     }
  1342. }
  1343.  
  1344. int CSaveAttachmentStream::Write(const char *lpBuf, int32 nCount)
  1345. {
  1346.     if (nCount == 0)
  1347.         return 0;
  1348.  
  1349.     SetupINTLConverter();
  1350.  
  1351.     char *newStr = NULL;
  1352.     if(m_doConvert)
  1353.     {
  1354.         char *dupStr = (char *) XP_ALLOC(nCount+1);
  1355.         if (dupStr)
  1356.         {
  1357.             XP_MEMCPY(dupStr, lpBuf, nCount);
  1358.             *(dupStr + nCount) = 0;
  1359.             newStr = (char *) INTL_CallCharCodeConverter(m_converter,
  1360.                                                 (unsigned char *)dupStr,
  1361.                                                 nCount);
  1362.             if (!newStr)
  1363.                 newStr = dupStr;
  1364.             else if (newStr != dupStr)
  1365.                 XP_FREE(dupStr);
  1366.  
  1367.             if (newStr)
  1368.             {
  1369.                 lpBuf = newStr;
  1370.                 nCount = INTL_GetCCCLen(m_converter);
  1371.             }
  1372.         }
  1373.     }
  1374.  
  1375.     if (m_nPosition + nCount > m_nBufferSize)
  1376.         Grow(m_nPosition + nCount);
  1377.  
  1378.     ASSERT(m_nPosition + nCount <= m_nBufferSize);
  1379.  
  1380.     // Safety net
  1381.     if (m_nPosition + nCount > m_nBufferSize)
  1382.     {
  1383.         XP_FREEIF(newStr);
  1384.         return 0;
  1385.     }
  1386.  
  1387.     BYTE *lpBuffer = (BYTE *)::GlobalLock(m_hGlobal);
  1388.     memcpy((BYTE*)lpBuffer + m_nPosition, (BYTE*)lpBuf, nCount);
  1389.     ::GlobalUnlock(m_hGlobal);
  1390.  
  1391.     m_nPosition += nCount;
  1392.     if (m_nPosition > m_nFileSize)
  1393.         m_nFileSize = m_nPosition;
  1394.  
  1395.     XP_FREEIF(newStr);
  1396.     return (int) nCount;
  1397. }
  1398.  
  1399. void CSaveAttachmentStream::Complete()
  1400. {
  1401.     BOOL res = SetSize(m_nFileSize);
  1402.  
  1403.     if (m_pbStatus)
  1404.         *m_pbStatus = res;
  1405. }
  1406.  
  1407. void CSaveAttachmentStream::Abort(int iStatus)
  1408. {
  1409.     if (m_pbStatus)
  1410.         *m_pbStatus = FALSE;
  1411. }
  1412.  
  1413. BOOL CSaveAttachmentStream::Grow(int32 newLen)
  1414. {
  1415.     if (newLen > m_nBufferSize)
  1416.     {
  1417.         // grow the buffer
  1418.         int32 newBufferSize = m_nBufferSize;
  1419.  
  1420.         // determine new buffer size
  1421.         while (newBufferSize < newLen)
  1422.             newBufferSize += 4096;
  1423.  
  1424.         // allocate new buffer
  1425.         HGLOBAL hNew = ::GlobalReAlloc(m_hGlobal, newBufferSize, GMEM_MOVEABLE|GMEM_ZEROINIT);
  1426.  
  1427.         if (hNew == NULL)
  1428.             return FALSE;
  1429.  
  1430.         m_nBufferSize = newBufferSize;
  1431.     }
  1432.     return TRUE;
  1433. }
  1434.  
  1435. BOOL CSaveAttachmentStream::SetSize(int32 nSize)
  1436. {
  1437.     if (nSize < 1)
  1438.         return FALSE;
  1439.     
  1440.     HGLOBAL hNew = ::GlobalReAlloc(m_hGlobal, nSize, GMEM_MOVEABLE);
  1441.     
  1442.     return (hNew != NULL);
  1443. }
  1444.  
  1445. BOOL CSaveCX::SaveToGlobal(HGLOBAL *phGlobal, LPCSTR lpszUrl, LPCSTR lpszTitle)
  1446. {
  1447.     //    Indirect constructor for serializing object.
  1448.     BOOL bRes = TRUE;
  1449.     *phGlobal = ::GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, 4096);
  1450.  
  1451.     // This would be too sad...
  1452.     if (!*phGlobal)
  1453.         return FALSE;
  1454.  
  1455.     //    Allocate the dialog.
  1456.     CSaveCX *pSaveCX = new CSaveCX(lpszUrl, NULL, NULL);
  1457.  
  1458.     pSaveCX->m_bSavingToGlobal = TRUE;
  1459.     pSaveCX->m_csFileName = lpszTitle;
  1460.  
  1461.     CSaveAttachmentStream *pStream = 
  1462.         new CSaveAttachmentStream(pSaveCX->GetContext(), *phGlobal, lpszUrl, lpszTitle, &bRes);
  1463.  
  1464.     pSaveCX->SetSecondaryStream(pStream->GetStream());
  1465.  
  1466.     if (pSaveCX->CanCreate()) {
  1467.         pSaveCX->DoCreate();
  1468.     } else {
  1469.         return FALSE;
  1470.     }
  1471.  
  1472.     FEU_BlockUntilDestroyed(pSaveCX->GetContextID());
  1473.  
  1474.     delete pStream;
  1475.  
  1476.     if (!bRes) {
  1477.         ::GlobalFree(*phGlobal);
  1478.         *phGlobal = NULL;
  1479.     }
  1480.  
  1481.     return bRes;
  1482. }
  1483.  
  1484.