mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1662 lines
38 KiB
1662 lines
38 KiB
|
|
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Eric W. Sink [email protected]
|
|
Jim Seidman [email protected]
|
|
Scott Piette [email protected]
|
|
*/
|
|
|
|
|
|
#include <all.h>
|
|
#include <dlgs.h>
|
|
#include <intshcut.h>
|
|
#include <commdlg.h>
|
|
#include "history.h"
|
|
|
|
|
|
//#ifdef FEATURE_INTL // isspace doesn't work with non-ascii characters
|
|
#undef isspace
|
|
#define isspace(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r')||(c=='\v')|| \
|
|
(c=='\f'))
|
|
//#endif
|
|
|
|
static int GHist_Add_Hash(PSTR pszURL, PSTR pszFile, time_t tm);
|
|
static int GHist_Export(char *file, int history_expire_days, int iHistoryNumPlaces);
|
|
static void GHist_DeleteIndexedItem(int ndx, BOOL fDelFile);
|
|
static void GetHostPathFromURL(PCSTR pcszURL, PSTR pszHostPath);
|
|
static void GetFriendlyFilenameFromURL(
|
|
PCSTR pcszURL,
|
|
PSTR pszFFn,
|
|
int iFFnLen,
|
|
PSTR pszHostPath,
|
|
DWORD dwFlags);
|
|
|
|
static BOOL FCreateScDir(PCSTR szScDir);
|
|
|
|
far struct hash_table gGlobalHistory;
|
|
|
|
extern void GTR_RefreshHistory(void);
|
|
|
|
/*
|
|
static int GHist_Add_NoSession( char *url,
|
|
char *title,
|
|
time_t tm,
|
|
BOOL fCreateShortcut);
|
|
*/
|
|
|
|
#define TITLE_LEN 512
|
|
|
|
struct _HTStructured
|
|
{
|
|
CONST HTStructuredClass *isa;
|
|
CONST SGML_dtd *dtd;
|
|
|
|
BOOL bInAnchor;
|
|
char href[MAX_URL_STRING + 1];
|
|
char title[TITLE_LEN + 1]; /* TODO check for overflow */
|
|
int lenTitle;
|
|
char base_url[MAX_URL_STRING + 1];
|
|
time_t tm;
|
|
};
|
|
|
|
/* Flush Buffer
|
|
*/
|
|
PRIVATE void HTGHist_flush(HTStructured * me)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
/* Character handling
|
|
*/
|
|
PRIVATE void HTGHist_put_character(HTStructured * me, char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '\n':
|
|
case '\t':
|
|
case '\r':
|
|
c = ' ';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (me->bInAnchor && (me->lenTitle < TITLE_LEN))
|
|
{
|
|
me->title[me->lenTitle++] = c;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* String handling
|
|
*/
|
|
PRIVATE void HTGHist_put_string(HTStructured * me, CONST char *s)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
PRIVATE void HTGHist_write(HTStructured * me, CONST char *s, int l)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
/* Start Element
|
|
*/
|
|
PRIVATE void HTGHist_start_element(HTStructured * me, int element_number, CONST BOOL * present, CONST char **value)
|
|
{
|
|
switch (element_number)
|
|
{
|
|
case HTML_A:
|
|
{
|
|
if (present[HTML_A_HREF])
|
|
{
|
|
GTR_strncpy(me->href, value[HTML_A_HREF], MAX_URL_STRING);
|
|
HTSimplify(me->href);
|
|
}
|
|
else
|
|
{
|
|
me->href[0] = '\0';
|
|
}
|
|
if (present[HTML_A_NAME])
|
|
{
|
|
me->tm = atol(value[HTML_A_NAME]);
|
|
}
|
|
else
|
|
{
|
|
me->tm = time(NULL);
|
|
}
|
|
|
|
memset(me->title, 0, TITLE_LEN + 1);
|
|
me->lenTitle = 0;
|
|
me->bInAnchor = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* End Element
|
|
*/
|
|
PRIVATE void HTGHist_end_element(HTStructured * me, int element_number)
|
|
{
|
|
char *full_address;
|
|
char mycopy[MAX_URL_STRING + 1];
|
|
char *stripped;
|
|
|
|
switch (element_number)
|
|
{
|
|
case HTML_A:
|
|
/*
|
|
First get the full URL
|
|
*/
|
|
GTR_strncpy(mycopy, me->href, MAX_URL_STRING);
|
|
|
|
stripped = HTStrip(mycopy);
|
|
full_address = HTParse(stripped,
|
|
me->base_url,
|
|
PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION | PARSE_ANCHOR);
|
|
|
|
GHist_Add_Hash(full_address, me->lenTitle ? me->title : NULL, me->tm);
|
|
|
|
GTR_FREE(full_address);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Expanding entities
|
|
*/
|
|
PRIVATE void HTGHist_put_entity(HTStructured * me, int entity_number)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free an HTML object
|
|
*/
|
|
PRIVATE void HTGHist_free(HTStructured * me)
|
|
{
|
|
|
|
GTR_FREE(me);
|
|
}
|
|
|
|
|
|
PRIVATE void HTGHist_abort(HTStructured * me, HTError e)
|
|
{
|
|
HTGHist_free(me);
|
|
}
|
|
|
|
|
|
/* Structured Object Class
|
|
*/
|
|
PRIVATE CONST HTStructuredClass HTGlobalHistory = /* As opposed to print etc */
|
|
{
|
|
"HTMLToGlobalHistory",
|
|
HTGHist_free,
|
|
HTGHist_abort,
|
|
HTGHist_put_character, HTGHist_put_string, HTGHist_write,
|
|
HTGHist_start_element, HTGHist_end_element,
|
|
HTGHist_put_entity, NULL, NULL, NULL
|
|
};
|
|
|
|
|
|
/* HTConverter from HTML to internal global history structure
|
|
*/
|
|
PUBLIC HTStream *HTMLToGlobalHistory(struct Mwin *tw, HTRequest * request, void *param, HTFormat input_format, HTFormat output_format, HTStream * output_stream)
|
|
{
|
|
HTStructured *me = (HTStructured *) GTR_CALLOC(1, sizeof(*me));
|
|
if (me)
|
|
{
|
|
GTR_strncpy(me->base_url, request->destination->szActualURL, MAX_URL_STRING);
|
|
me->bInAnchor = FALSE;
|
|
me->isa = (HTStructuredClass *) & HTGlobalHistory;
|
|
me->dtd = &HTMLP_dtd;
|
|
return SGML_new(tw, &HTMLP_dtd, me, request);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static int GHist_Add_Hash( PSTR pszURL, PSTR pszFile, time_t tm)
|
|
{
|
|
int ndx;
|
|
time_t old_tm;
|
|
int err;
|
|
|
|
if (!pszURL || !*pszURL || !pszFile || !*pszFile)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ndx = Hash_Find(&gGlobalHistory, pszURL, NULL, (void **) &old_tm);
|
|
if (ndx >= 0)
|
|
{
|
|
/* already added, return */
|
|
return 0;
|
|
}
|
|
|
|
XX_DMsg(DBG_HIST, ("Adding to global history: %s\n", pszURL));
|
|
err = Hash_Add(&gGlobalHistory, pszURL, pszFile, (void *)tm);
|
|
|
|
return err;
|
|
}
|
|
|
|
struct Params_GHist_Load {
|
|
HTRequest *request;
|
|
|
|
/* Used internally */
|
|
int status;
|
|
};
|
|
|
|
static int GHist_Load_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_GHist_Load *pParams;
|
|
struct Params_LoadAsync *p2;
|
|
|
|
pParams = *ppInfo;
|
|
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
pParams->request = HTRequest_validate(pParams->request);
|
|
if (pParams->request == NULL) return STATE_DONE;
|
|
p2 = GTR_MALLOC(sizeof(*p2));
|
|
p2->request = pParams->request;
|
|
p2->pStatus = &pParams->status;
|
|
Async_DoCall(HTLoadDocument_Async, p2);
|
|
return STATE_OTHER;
|
|
case STATE_OTHER:
|
|
case STATE_ABORT:
|
|
pParams->request = HTRequest_validate(pParams->request);
|
|
if (pParams->request)
|
|
{
|
|
Dest_DestroyDest(pParams->request->destination);
|
|
HTRequest_delete(pParams->request);
|
|
pParams->request = NULL;
|
|
}
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
return STATE_DONE;
|
|
}
|
|
|
|
void GHist_Init(void)
|
|
{
|
|
HTRequest *request;
|
|
char url[MAX_URL_STRING + 1];
|
|
struct Params_GHist_Load *phll;
|
|
struct DestInfo *pDest;
|
|
BOOL fExistsHist;
|
|
char path[_MAX_PATH+1];
|
|
PSTR pszT;
|
|
|
|
if (GetInternetScDir(path, ID_HISTORY) != S_OK)
|
|
return;
|
|
pszT = path + strlen(path);
|
|
if (!(pszT > path && *(pszT-1) == chBSlash))
|
|
*pszT++ = chBSlash;
|
|
strcpy(pszT, gPrefs.szGlobHistFile); /* the globhist file must be a basename only */
|
|
|
|
strcpy(url,path);
|
|
fExistsHist = FExistsFile(url, FALSE, FALSE);
|
|
|
|
FixPathName(path);
|
|
strcpy(url, "file:///");
|
|
strcat(url, path);
|
|
|
|
Hash_Init(&gGlobalHistory);
|
|
if (fExistsHist)
|
|
{
|
|
pDest = Dest_CreateDest(url);
|
|
if (pDest)
|
|
{
|
|
request = HTRequest_new();
|
|
HTFormatInit(request->conversions);
|
|
request->output_format = HTAtom_for("www/global_history");
|
|
request->destination = pDest;
|
|
|
|
phll = GTR_MALLOC(sizeof(*phll));
|
|
phll->request = request;
|
|
Async_StartThread(GHist_Load_Async, phll, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Given a file, and an ID for the folder (currently only FOLDER_HISTORY
|
|
* or FOLDER_CACHE), return the full path of the file.
|
|
*/
|
|
void GetFullPath(PSTR pszFull, PCSTR pcszFile, PCSTR pcszDir, FOLDER folder, int iLenMax)
|
|
{
|
|
char *pszT;
|
|
|
|
XX_Assert(!pcszDir || folder == FOLDER_CACHE || folder == FOLDER_HISTORY, (""));
|
|
strncpy(pszFull,
|
|
pcszDir ? pcszDir
|
|
: folder == FOLDER_CACHE ?
|
|
gPrefs.szCacheLocation
|
|
: gPrefs.szHistoryLocation,
|
|
iLenMax-2);
|
|
pszFull[iLenMax-1]=0;
|
|
|
|
pszT = pszFull + strlen(pszFull);
|
|
/* Append a backslash if not already present */
|
|
if (!(pszT > pszFull && *(pszT-1) == chBSlash))
|
|
{
|
|
*pszT++ = chBSlash;
|
|
*pszT = '\0';
|
|
}
|
|
XX_Assert(pcszFile, ("Null filename"));
|
|
/* strncat ensures zero termination */
|
|
strncat(pszFull, pcszFile, iLenMax - (pszT - pszFull));
|
|
}
|
|
|
|
void UpdateHistoryLocation(PCSTR pszNewLoc)
|
|
{
|
|
char szMsg[MAX_NAME_STRING+1];
|
|
char szCur[_MAX_PATH+1], szNew[_MAX_PATH+1];
|
|
char szHistLoc[_MAX_PATH+1];
|
|
int i, cFiles;
|
|
PSTR pszScFile;
|
|
time_t tm;
|
|
HCURSOR hCursorPrev=NULL;
|
|
|
|
extern HCURSOR hCursorWorking;
|
|
|
|
if (GetInternetScDir(szHistLoc, ID_HISTORY) != S_OK)
|
|
return;
|
|
if (!lstrcmpi(szHistLoc, pszNewLoc))
|
|
return;
|
|
|
|
if (!FExistsDir(pszNewLoc, TRUE, FALSE))
|
|
{
|
|
GTR_formatmsg(RES_STRING_NO_DIR,szMsg,sizeof(szMsg),pszNewLoc);
|
|
MessageBox(wg.hWndHidden, szMsg, NULL, MB_OK | MB_ICONEXCLAMATION);
|
|
return;
|
|
}
|
|
|
|
if (hCursorWorking)
|
|
hCursorPrev = SetCursor(hCursorWorking);
|
|
|
|
GetFullDcPath(szCur, gPrefs.szGlobHistFile, szHistLoc, _MAX_PATH+1);
|
|
GetFullDcPath(szNew, gPrefs.szGlobHistFile, pszNewLoc, _MAX_PATH+1);
|
|
MoveFile(szCur, szNew);
|
|
for (i=0, cFiles=Hash_Count(&gGlobalHistory); i<cFiles; i++)
|
|
{
|
|
Hash_GetIndexedEntry(&gGlobalHistory, i, NULL, &pszScFile, (void **)&tm);
|
|
if (!pszScFile)
|
|
continue;
|
|
GetFullHistPath(szCur, pszScFile, NULL, _MAX_PATH+1);
|
|
if (!FExistsFile(szCur, FALSE, NULL))
|
|
continue;
|
|
GetFullHistPath(szNew, pszScFile, pszNewLoc, _MAX_PATH+1);
|
|
if (!MoveFile(szCur, szNew))
|
|
{
|
|
GTR_formatmsg(RES_STRING_CANT_MOVE_FILE, szMsg, sizeof(szMsg), szCur, szNew);
|
|
MessageBox(wg.hWndHidden, szMsg, NULL, MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
|
|
/* RemoveDirectory will fail if dir. is not empty */
|
|
RemoveDirectory(gPrefs.szHistoryLocation);
|
|
strcpy(gPrefs.szHistoryLocation, pszNewLoc);
|
|
|
|
if (hCursorPrev)
|
|
SetCursor(hCursorPrev);
|
|
}
|
|
|
|
/*
|
|
* pcszFile: Full path of the history file
|
|
*/
|
|
static BOOL FInGlobHist(PCSTR pcszFile)
|
|
{
|
|
int i, iMax;
|
|
PSTR pszFileHash;
|
|
time_t tm;
|
|
|
|
iMax = Hash_Count(&gGlobalHistory);
|
|
for (i=0; i < iMax; i++)
|
|
{
|
|
Hash_GetIndexedEntry(&gGlobalHistory, i, NULL, &pszFileHash, (void **)&tm);
|
|
if (pszFileHash && !lstrcmpi(pcszFile, pszFileHash))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void GHist_SortAndPrune(int iNumPlacesMax, int history_expire_days)
|
|
{
|
|
char const cszFileFilterB[]="\\*";
|
|
|
|
int i, age, cDel;
|
|
time_t then, now;
|
|
PSTR pszScFile;
|
|
BOOL bKeep;
|
|
char szHist[_MAX_PATH+1];
|
|
WIN32_FIND_DATA wfd;
|
|
HANDLE hFind;
|
|
|
|
now = time(NULL);
|
|
|
|
for (i = Hash_Count(&gGlobalHistory)-1; i>=0; i--)
|
|
{
|
|
Hash_GetIndexedEntry(&gGlobalHistory, i, NULL, &pszScFile, (void **)&then);
|
|
|
|
if (!pszScFile)
|
|
{
|
|
bKeep = FALSE;
|
|
goto LDelEntry;
|
|
}
|
|
GetFullHistPath(szHist, pszScFile, NULL, _MAX_PATH+1);
|
|
if (!FExistsFile(szHist, FALSE, NULL))
|
|
{
|
|
bKeep = FALSE;
|
|
}
|
|
else if (history_expire_days == 0)
|
|
{
|
|
/*
|
|
If expire == 0, don't save anything
|
|
*/
|
|
bKeep = FALSE;
|
|
}
|
|
else if (history_expire_days > 0)
|
|
{
|
|
age = (now - then) / (24 * 60 * 60);
|
|
if (age > history_expire_days)
|
|
bKeep = FALSE;
|
|
else
|
|
bKeep = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
If expire < 0, then keep everything
|
|
*/
|
|
bKeep = TRUE;
|
|
}
|
|
|
|
if (!bKeep)
|
|
{
|
|
DeleteFile(szHist);
|
|
LDelEntry:
|
|
Hash_DeleteIndexedEntry(&gGlobalHistory, i);
|
|
}
|
|
}
|
|
|
|
Hash_SortByDataDescending(&gGlobalHistory);
|
|
|
|
i=Hash_Count(&gGlobalHistory);
|
|
cDel = max(0, i - iNumPlacesMax);
|
|
for (i--; cDel; cDel--, i--)
|
|
GHist_DeleteIndexedItem(i, /*fDelFile=*/TRUE);
|
|
|
|
XX_Assert(Hash_Count(&gGlobalHistory) <= iNumPlacesMax, (""));
|
|
|
|
if (GetInternetScDir(szHist, ID_HISTORY) != S_OK)
|
|
return;
|
|
strcat(szHist, cszFileFilterB);
|
|
|
|
/* Now go and delete all spurious files from the hist. dir */
|
|
if ((hFind = FindFirstFile(szHist, &wfd)) == INVALID_HANDLE_VALUE)
|
|
return;
|
|
do
|
|
{
|
|
if ( FInGlobHist(wfd.cFileName)
|
|
|| wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
continue;
|
|
GetFullHistPath(szHist, wfd.cFileName, NULL, _MAX_PATH+1);
|
|
DeleteFile(szHist);
|
|
} while (FindNextFile(hFind, &wfd));
|
|
|
|
FindClose(hFind);
|
|
}
|
|
|
|
static int GHist_Export(char *file, int history_expire_days, int iHistoryNumPlaces)
|
|
{
|
|
int count;
|
|
char *s1;
|
|
char *s2;
|
|
FILE *fp;
|
|
int i;
|
|
time_t then;
|
|
|
|
GHist_SortAndPrune(iHistoryNumPlaces, history_expire_days);
|
|
|
|
fp = fopen(file, "w");
|
|
if (!fp)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
fprintf(fp, "<title>Global History</title>\n");
|
|
fprintf(fp, "\n<h1>Global History Page</h1>\n");
|
|
count = Hash_Count(&gGlobalHistory);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
Hash_GetIndexedEntry(&gGlobalHistory, i, &s1, &s2, (void **)&then);
|
|
if (!s1 || !*s1 || !s2 || !*s2)
|
|
continue;
|
|
fprintf(fp, "<a href=\"%s\" name=\"%lu\">%s</a><p>\n", s1, (unsigned long) then, s2);
|
|
}
|
|
fclose(fp);
|
|
SetFileAttributes(file, FILE_ATTRIBUTE_HIDDEN);
|
|
return 0;
|
|
}
|
|
|
|
int GHist_SaveToDisk(void)
|
|
{
|
|
char path[_MAX_PATH];
|
|
int status;
|
|
PSTR pszT;
|
|
|
|
if (GetInternetScDir(path, ID_HISTORY) != S_OK)
|
|
return -1;
|
|
pszT = path + strlen(path);
|
|
if (!(pszT > path && *(pszT-1) == chBSlash))
|
|
*pszT++ = chBSlash;
|
|
strcpy(pszT, gPrefs.szGlobHistFile); /* the globhist file must be a basename only */
|
|
status = GHist_Export(path, gPrefs.history_expire_days, gPrefs.iHistoryNumPlaces);
|
|
return status;
|
|
}
|
|
|
|
void GHist_Destroy(void)
|
|
{
|
|
Hash_FreeContents(&gGlobalHistory);
|
|
}
|
|
|
|
static void GHist_DeleteIndexedItem(int ndx, BOOL fDelFile)
|
|
{
|
|
PSTR pszScFile;
|
|
time_t tm;
|
|
char szHist[_MAX_PATH+1];
|
|
|
|
Hash_GetIndexedEntry(&gGlobalHistory, ndx, NULL, &pszScFile, (void **)&tm);
|
|
if (fDelFile && pszScFile && *pszScFile)
|
|
{
|
|
GetFullHistPath(szHist, pszScFile, NULL, _MAX_PATH+1);
|
|
DeleteFile(szHist);
|
|
}
|
|
Hash_DeleteIndexedEntry(&gGlobalHistory, ndx);
|
|
}
|
|
|
|
|
|
static int GHist_Add_NoSession( char *url,
|
|
char *title,
|
|
time_t tm,
|
|
BOOL fCreateShortcut)
|
|
{
|
|
int ndx;
|
|
time_t old_tm;
|
|
int err;
|
|
char szScFile[_MAX_PATH+1];
|
|
PSTR pszFileHash=NULL;
|
|
|
|
if (title)
|
|
{
|
|
if (fCreateShortcut)
|
|
{
|
|
/* Don't remove whitespace from title if fCreateShort=FALSE because
|
|
* title is the filename, which can start with a space.
|
|
*/
|
|
while (isspace(*title))
|
|
{
|
|
title++;
|
|
}
|
|
}
|
|
}
|
|
if (!title || !*title)
|
|
{
|
|
title = url;
|
|
}
|
|
|
|
ndx = Hash_Find(&gGlobalHistory, url, NULL, (void **) &old_tm);
|
|
if (ndx >= 0)
|
|
{
|
|
/* don't free pgHistInfo */
|
|
GHist_DeleteIndexedItem(ndx, fCreateShortcut);
|
|
}
|
|
|
|
if (fCreateShortcut)
|
|
{
|
|
/* szScFile will get filled in. */
|
|
CreateURLShortcut(url, title, szScFile, FOLDER_HISTORY, 0);
|
|
pszFileHash = szScFile;
|
|
}
|
|
XX_DMsg(DBG_HIST, ("Adding to global history: %s\n", url));
|
|
err = Hash_Add(&gGlobalHistory, url, pszFileHash, (void *)tm);
|
|
|
|
GHist_SortAndPrune(gPrefs.iHistoryNumPlaces, gPrefs.history_expire_days);
|
|
|
|
return err;
|
|
}
|
|
|
|
int GHist_Add(char *url, char *title, time_t tm, BOOL fCreateShortcut)
|
|
{
|
|
int err;
|
|
|
|
|
|
if (title)
|
|
{
|
|
if (fCreateShortcut)
|
|
{
|
|
/* Don't remove whitespace from title if fCreateShort=FALSE because
|
|
* title is the filename, which can start with a space.
|
|
*/
|
|
while (isspace(*title))
|
|
{
|
|
title++;
|
|
}
|
|
}
|
|
}
|
|
if (!title || !*title)
|
|
{
|
|
title = url;
|
|
}
|
|
|
|
err = GHist_Add_NoSession(url, title, tm, fCreateShortcut);
|
|
|
|
#ifdef FEATURE_SPYGLASS_HOTLIST
|
|
GTR_RefreshHistory();
|
|
#endif // FEATURE_SPYGLASS_HOTLIST
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CreateURLShortcut( PCSTR pcszURL,
|
|
PCSTR pcszTitle,
|
|
PSTR pszScFileLeaf,
|
|
FOLDER folder,
|
|
DWORD dwFlags)
|
|
{
|
|
char szScFile[MAX_PATH+1];
|
|
HRESULT hr;
|
|
|
|
if ((hr = GetNewShortcutFilename(
|
|
pcszURL,
|
|
pcszTitle,
|
|
szScFile,
|
|
pszScFileLeaf,
|
|
folder,
|
|
dwFlags)) != S_OK)
|
|
{
|
|
if (pszScFileLeaf)
|
|
*pszScFileLeaf = '\0';
|
|
return hr;
|
|
}
|
|
|
|
return CreateNewURLShortcut(pcszURL, szScFile);
|
|
}
|
|
|
|
|
|
#define UpdateTimeStamp(pszFile) \
|
|
FExistsFile(pszFile, /*fUpdateTime=*/TRUE, NULL)
|
|
|
|
/* Max. number of files with the same name and a #n
|
|
* appended: ex. Filename#1, Filename#2,... Filename#50
|
|
*/
|
|
#define FILE_EXT_NUM_MAX 50
|
|
|
|
//const char cszShortcutsRoot[]="c:\\internet";
|
|
//const char cszHistoryDir[]="History";
|
|
//const char cszHotListDir[]="Favorites";
|
|
//const char cszShortcutsDirFmt[]="%s\\%s";
|
|
extern char const FAR cszURLExt[];
|
|
|
|
BOOL CALLBACK FnOFNHook(HWND hDlg, unsigned msg, WPARAM wParam, LONG lParam);
|
|
|
|
static void AddURLExt(PSTR pszScFile)
|
|
{
|
|
PSTR pszT;
|
|
|
|
XX_Assert(pszScFile, (""));
|
|
if ( !(pszT = strrchr(pszScFile, chPeriod))
|
|
|| lstrcmpi(pszT, (LPSTR)cszURLExt))
|
|
{
|
|
/* Add .url ext */
|
|
strcat(pszScFile, (LPSTR)cszURLExt);
|
|
}
|
|
}
|
|
|
|
BOOL NEAR PASCAL FnOFNHookNotify(HWND hDlg, LPOFNOTIFY pofn)
|
|
{
|
|
char szFile[MAX_PATH];
|
|
|
|
switch (pofn->hdr.code)
|
|
{
|
|
case CDN_SELCHANGE:
|
|
{
|
|
if (CommDlg_OpenSave_GetSpec(GetParent(hDlg),
|
|
szFile, sizeof(szFile)) <= sizeof(szFile))
|
|
{
|
|
SetDlgItemText(hDlg, 100, szFile);
|
|
}
|
|
|
|
if (CommDlg_OpenSave_GetFilePath(GetParent(hDlg),
|
|
szFile, sizeof(szFile)) <= sizeof(szFile))
|
|
{
|
|
SetDlgItemText(hDlg, 102, szFile);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case CDN_FOLDERCHANGE:
|
|
{
|
|
if (CommDlg_OpenSave_GetFolderPath(GetParent(hDlg),
|
|
szFile, sizeof(szFile)) <= sizeof(szFile))
|
|
{
|
|
SetDlgItemText(hDlg, 101, szFile);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CDN_FILEOK:
|
|
{
|
|
SetWindowLong(hDlg, DWL_MSGRESULT, FALSE); //quit
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL CALLBACK FnOFNHook(HWND hDlg, unsigned msg, WPARAM wParam, LONG lParam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
char szT[100];
|
|
|
|
// lParam is lpOFN
|
|
// SetWindowLong(hDlg, DWL_USER, lParam);
|
|
if (LoadString(wg.hInstance, RES_STRING_FAVS_FOLDER, szT, sizeof(szT)-1) != 0)
|
|
CommDlg_OpenSave_SetControlText(GetParent(hDlg), stc4, szT);
|
|
if (LoadString(wg.hInstance, RES_STRING_FAVS_NAME, szT, sizeof(szT)-1) != 0)
|
|
CommDlg_OpenSave_SetControlText(GetParent(hDlg), stc3, szT);
|
|
if (LoadString(wg.hInstance, RES_STRING_FAVS_ADD, szT, sizeof(szT)-1) != 0)
|
|
CommDlg_OpenSave_SetControlText(GetParent(hDlg), IDOK, szT);
|
|
/* Hide the "Save as Type" text box */
|
|
CommDlg_OpenSave_HideControl(GetParent(hDlg), stc2);
|
|
/* Hide the listbox with save type extensions */
|
|
CommDlg_OpenSave_HideControl(GetParent(hDlg), cmb1);
|
|
/* Hide the Open as read-only control */
|
|
CommDlg_OpenSave_HideControl(GetParent(hDlg), chx1);
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
return(FnOFNHookNotify(hDlg, (LPOFNOTIFY)lParam));
|
|
|
|
case WM_ENTERIDLE:
|
|
main_EnterIdle(hDlg, wParam);
|
|
return 0;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* pszScFile: contains full path of shortcut file.
|
|
* pszFileLeaf: contains only the filename without directory path
|
|
*/
|
|
static HRESULT DoFavSaveAsDlg(PSTR pszScFile, PSTR pszFileLeaf)
|
|
{
|
|
const char cszUrlExt[] = "url";
|
|
char szFile[MAX_PATH];
|
|
char szTitle[MAX_PATH];
|
|
extern HWND hwndActiveFrame;
|
|
|
|
OPENFILENAME ofn =
|
|
{
|
|
sizeof(OPENFILENAME), // lStructSize;
|
|
wg.hWndHidden, // hwndOwner;
|
|
wg.hInstance, // hInstance;
|
|
NULL, // lpstrFilter;
|
|
NULL, // lpstrCustomFilter;
|
|
0, // nMaxCustFilter;
|
|
0, // nFilterIndex;
|
|
szFile, // lpstrFile;
|
|
sizeof(szFile), // nMaxFile;
|
|
NULL, // lpstrFileTitle;
|
|
0, // nMaxFileTitle;
|
|
pszScFile, // lpstrInitialDir;
|
|
NULL, // lpstrTitle; Title of dlg.
|
|
OFN_EXPLORER | OFN_ENABLEHOOK
|
|
| OFN_OVERWRITEPROMPT, // flags
|
|
0, // nFileOffset;
|
|
0, // nFileExtension;
|
|
cszUrlExt, // lpstrDefExt;
|
|
0, // lCustData;
|
|
FnOFNHook, // lpfnHook;
|
|
NULL, // lpTemplateName;
|
|
};
|
|
#ifdef DAYTONA_BUILD
|
|
/*
|
|
** If we are on NT 3.51 change the flags so that the explorer is not referenced
|
|
*/
|
|
if(OnNT351) {
|
|
int i = 0;
|
|
char pszFilterString[] = "URL Files(*.url)|*.url|";
|
|
|
|
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_LONGNAMES;
|
|
|
|
for(i = 0; pszFilterString[i] != '\0'; i++) {
|
|
if(pszFilterString[i] == '|')
|
|
pszFilterString[i] = '\0';
|
|
}
|
|
ofn.lpstrFilter =pszFilterString;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrDefExt = "url";
|
|
ofn.lpfnHook = NULL;
|
|
}
|
|
#endif
|
|
if (hwndActiveFrame)
|
|
ofn.hwndOwner = hwndActiveFrame;
|
|
|
|
if (LoadString(wg.hInstance, RES_STRING_FAVS_TITLE, szTitle, sizeof(szTitle)-1) == 0)
|
|
*szTitle = '\0';
|
|
ofn.lpstrTitle = szTitle;
|
|
/* HACK! HACK! We know that pszScFile starts with the Favs. dir.
|
|
* and pszFileLeaf is the leaf filename with a backslash just before it.
|
|
*/
|
|
XX_Assert(pszScFile < pszFileLeaf, (""));
|
|
XX_Assert(pszFileLeaf < pszScFile + strlen(pszScFile), (""));
|
|
XX_Assert(*(pszFileLeaf-1)==chBSlash, (""));
|
|
*(pszFileLeaf-1) = '\0';
|
|
|
|
strcpy(szFile, pszFileLeaf);
|
|
if (TW_GetSaveFileName(&ofn))
|
|
{
|
|
strcpy(pszScFile, szFile);
|
|
AddURLExt(pszScFile); //tack on .url if needed
|
|
return S_OK;
|
|
}
|
|
return E_ABORT;
|
|
}
|
|
|
|
HRESULT GetInternetScDir(PSTR pszDir, UINT wId)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT found;
|
|
char szFav[MAX_PATH+1];
|
|
const char cszBrowserWinKey[]="Explorer\\User Shell Folders";
|
|
const char cszFavValName[]="Favorites";
|
|
extern const char szBrowserWinKeyRoot[];
|
|
extern const char szBrowserIEKeyRoot[];
|
|
|
|
switch (wId)
|
|
{
|
|
case ID_HOTLIST:
|
|
// look in HKEY_LOCAL_MACHINE
|
|
setKeyRoot(szBrowserIEKeyRoot);
|
|
found = regGetPrivateProfileString("Main", (PSTR)cszFavValName,
|
|
"not found", pszDir, MAX_PATH, HKEY_LOCAL_MACHINE);
|
|
if(found == ERROR_SUCCESS)
|
|
{
|
|
if (FExistsDir(pszDir, TRUE, TRUE))
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("GetInternetScDir: \"%s\" exists\n", pszDir));
|
|
// then write it out to the registry
|
|
setKeyRoot(szBrowserWinKeyRoot);
|
|
regWritePrivateProfileString((PSTR)cszBrowserWinKey,
|
|
(PSTR)cszFavValName, pszDir, HKEY_CURRENT_USER);
|
|
setKeyRoot(szBrowserIEKeyRoot);
|
|
}
|
|
else
|
|
XX_DMsg(DBG_IMAGE, ("GetInternetScDir: \"%s\" does not exist\n", pszDir));
|
|
}
|
|
else
|
|
{
|
|
/* Can't use GetShellFolderPath because we want fCreate=TRUE */
|
|
if (!SHGetSpecialFolderPath(NULL, pszDir, CSIDL_FAVORITES, TRUE))
|
|
{
|
|
/* Create favs. dir off of Windows dir if not already present */
|
|
if ( GetWindowsDirectory(pszDir, MAX_PATH) <= 0
|
|
|| !LoadString(wg.hInstance, RES_STRING_FAVORITES, szFav, MAX_PATH))
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* if pszDir ends in \, don't skip the starting \ in szFav */
|
|
XX_Assert(*szFav == chBSlash, (""));
|
|
strcat(pszDir, szFav + ((pszDir[strlen(pszDir)-1] == chBSlash) ? 1 : 0));
|
|
if (!FExistsDir(pszDir, TRUE, FALSE))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
setKeyRoot(szBrowserWinKeyRoot);
|
|
regWritePrivateProfileString((PSTR)cszBrowserWinKey,
|
|
(PSTR)cszFavValName, pszDir, HKEY_CURRENT_USER);
|
|
setKeyRoot(szBrowserIEKeyRoot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if WINNT
|
|
/* NTHACK -sc If unicode, use SHGetSpecialFolderPath to get
|
|
* unicode result and create dir if necessary, then call
|
|
* GetShellFolderPath to get ansi information.
|
|
* SHGetSpecialFolderPath on NT should be available in ansi soon.
|
|
*/
|
|
if (pszDir && pszDir[1] == 0)
|
|
GetShellFolderPath(NULL, CSIDL_FAVORITES, pszDir);
|
|
#endif /* WINNT */
|
|
XX_DMsg(DBG_IMAGE, ("GetInternetScDir: \"%s\" found=0x%x\n", pszDir, found));
|
|
break;
|
|
|
|
case ID_HISTORY:
|
|
strcpy(pszDir, gPrefs.szHistoryLocation);
|
|
break;
|
|
|
|
default:
|
|
XX_Assert(FALSE, (""));
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*
|
|
* BUGBUG: (DavidDi 8/7/95) The length of pszScFile's buffer should be passed
|
|
* in here to avoid overflow.
|
|
*/
|
|
HRESULT GetNewShortcutFilename( PCSTR pcszURL,
|
|
PCSTR pcszTitle,
|
|
PSTR pszScFile,
|
|
PSTR pszScFileLeaf,
|
|
FOLDER folder,
|
|
DWORD dwFlags)
|
|
{
|
|
const char cszAppendixFmt_s[] = "%s.url";
|
|
const char cszAppendixFmt_si[] = "%s (%i).url";
|
|
|
|
char *pszScFileT;
|
|
char *pszFriendlyT;
|
|
char *pszFileLeaf;
|
|
char *pszExt;
|
|
char szURLExist[MAX_URL_STRING + 1];
|
|
char szFriendly[MAX_PATH+1];
|
|
char szURLHostPath[MAX_PATH+1];
|
|
BOOL fNoTitle, fHost;
|
|
int iExt;
|
|
HRESULT hr;
|
|
int len;
|
|
|
|
XX_Assert(pcszURL && pszScFile, ("URL strings NULL!"));
|
|
|
|
fNoTitle = (!pcszTitle || !lstrcmpi(pcszURL, pcszTitle));
|
|
|
|
switch (folder)
|
|
{
|
|
case FOLDER_NONE:
|
|
*pszScFile = '\0';
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case FOLDER_HISTORY:
|
|
hr = GetInternetScDir(pszScFile, ID_HISTORY);
|
|
break;
|
|
|
|
case FOLDER_FAVORITES:
|
|
hr = GetInternetScDir(pszScFile, ID_HOTLIST);
|
|
break;
|
|
|
|
case FOLDER_TEMP:
|
|
/*
|
|
* BUGBUG: (DavidDi 8/7/95) Assume pszScFile's buffer is at least
|
|
* MAX_PATH characters long.
|
|
*/
|
|
hr = (PREF_GetTempPath(MAX_PATH, pszScFile) > 0) ? S_OK : E_FAIL;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(folder == FOLDER_DESKTOP);
|
|
hr = GetShellFolderPath(NULL, CSIDL_DESKTOP, pszScFile);
|
|
break;
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
if (folder != FOLDER_NONE &&
|
|
!FExistsDir(pszScFile, /*fCreate=*/TRUE, /*fErrorMsg=*/TRUE))
|
|
return E_FAIL;
|
|
|
|
/* May needed szURLHostPath later */
|
|
GetFriendlyFilenameFromURL(pcszURL, szFriendly, sizeof(szFriendly), szURLHostPath, 0);
|
|
if (!fNoTitle)
|
|
/* Ignore friendly URL, copy over title. */
|
|
strncpy(szFriendly, pcszTitle, sizeof(szFriendly) );
|
|
|
|
pszScFileT = pszScFile + (len = strlen(pszScFile));
|
|
len += 20 + lstrlen(szURLHostPath); // max extra chars: "/" + " (nn).url", 20 is plenty
|
|
szFriendly[max(0,sizeof(szFriendly)-1-len)] = 0;
|
|
// Note
|
|
|
|
/*
|
|
* BUGBUG: (DavidDi 4/24/95) This is broken for root folders. We will end
|
|
* up with two backslashes. Create the file name in a stack buffer, and
|
|
* CatPath() it on to the destination folder.
|
|
*/
|
|
if ( folder != FOLDER_NONE
|
|
&& !(pszScFileT > pszScFile && *(pszScFileT-1)==chBSlash))
|
|
*pszScFileT++ = chBSlash; // append a backslash if we don't already have one
|
|
pszFileLeaf = pszScFileT;
|
|
pszFriendlyT = szFriendly;
|
|
while (*pszFriendlyT)
|
|
{
|
|
unsigned char ch=*pszFriendlyT++;
|
|
|
|
#ifdef FEATURE_INTL
|
|
// NOTE : As far as creating shortcut, we just care for the platform's
|
|
// codepage not content's. IsDBCSLeadByte always returns NULL
|
|
// in SBCS codepage.
|
|
//
|
|
// REVIEW : We'll have to generate SBCS shortcut name for those SBCS platform
|
|
//
|
|
if (IsDBCSLeadByte(ch))
|
|
{
|
|
*pszScFileT++ = ch;
|
|
*pszScFileT++ = *pszFriendlyT++;
|
|
}
|
|
else
|
|
#endif
|
|
*pszScFileT++ = ChValidFilenameCh(ch);
|
|
}
|
|
|
|
/* Remember where we tacked on the url extension */
|
|
pszExt = pszScFileT;
|
|
hr = E_FAIL;
|
|
/* Now loop to either get the right .url file or
|
|
* get a filename that doesn't exist.
|
|
*/
|
|
iExt=0;
|
|
fHost=FALSE;
|
|
while (iExt < FILE_EXT_NUM_MAX)
|
|
{
|
|
/* Don't try to tack on szHostPath twice */
|
|
if (! fNoTitle || ! fHost)
|
|
{
|
|
PSTR pszSuffix;
|
|
|
|
if (IS_FLAG_CLEAR(dwFlags, NEWSHORTCUT_FL_NO_HOST_PATH))
|
|
pszSuffix = (fHost ? szURLHostPath : "");
|
|
else
|
|
pszSuffix = "";
|
|
|
|
if (!iExt)
|
|
/* Special case for 0 */
|
|
wsprintf(pszExt, cszAppendixFmt_s, pszSuffix);
|
|
else
|
|
wsprintf(pszExt, cszAppendixFmt_si, pszSuffix, iExt);
|
|
|
|
if (folder == FOLDER_FAVORITES)
|
|
{
|
|
hr = DoFavSaveAsDlg(pszScFile, pszFileLeaf);
|
|
break;
|
|
}
|
|
|
|
if (folder == FOLDER_NONE ||
|
|
!FExistsFile(pszScFile, /*fUpdateTime=*/FALSE, NULL))
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
if (IS_FLAG_CLEAR(dwFlags, NEWSHORTCUT_FL_ALLOW_DUPLICATE_URL))
|
|
{
|
|
if ( FGetURLString(pszScFile, szURLExist)
|
|
&& !lstrcmpi(szURLExist, pcszURL))
|
|
{
|
|
UpdateTimeStamp(pszScFile);
|
|
hr = S_OK; //should be E_ABORT?
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (IS_FLAG_CLEAR(dwFlags, NEWSHORTCUT_FL_NO_HOST_PATH))
|
|
{
|
|
iExt += fHost;
|
|
fHost = !fHost;
|
|
}
|
|
else
|
|
iExt++;
|
|
}
|
|
|
|
if (hr == S_OK && pszScFileLeaf)
|
|
strcpy(pszScFileLeaf, pszFileLeaf);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/* If in history hash, returns the title, else return friendly version
|
|
* of url.
|
|
*/
|
|
void GetFriendlyFromURL(PCSTR pcszURL, PSTR pszFFn, int iFFnLen, DWORD dwFlags)
|
|
{
|
|
PSTR pszTitle=NULL;
|
|
|
|
if (IS_FLAG_CLEAR(dwFlags,FRIENDLYURL_FL_NO_HASH_FIND)) {
|
|
if ( Hash_Find(&gGlobalHistory, pcszURL, &pszTitle, NULL) != -1
|
|
&& pszTitle)
|
|
strcpy(pszFFn, pszTitle);
|
|
}
|
|
if (!pszTitle)
|
|
GetFriendlyFilenameFromURL(pcszURL, pszFFn, iFFnLen, NULL, dwFlags);
|
|
}
|
|
|
|
/*
|
|
* Input: http://www.usb.ve/homepage.html
|
|
* Output: Homepage.htm (www.usb.ve)
|
|
*/
|
|
|
|
#define MIN_URL_CHARS_IN_FF_NAME 30 // When composing friendly filename that doesn't fit,
|
|
// use at least this many chars from URL
|
|
|
|
static void GetFriendlyFilenameFromURL(
|
|
PCSTR pcszURL,
|
|
PSTR pszFFn,
|
|
int iFFnLen,
|
|
PSTR pszHostPath,
|
|
DWORD dwFlags)
|
|
{
|
|
char *pszHostPathT;
|
|
char *pszFFnT;
|
|
char *pszLeafFile;
|
|
unsigned char ch;
|
|
# define pszParen pszLeafFile
|
|
#ifdef FEATURE_INTL
|
|
char *pszTemp;
|
|
#endif
|
|
|
|
XX_Assert(pcszURL && *pcszURL, ("NULL pcszURL!"));
|
|
#ifdef FEATURE_INTL
|
|
// REVIEW: Do we really have to care if the URL include double byte?
|
|
if (IsFECodePage(GetACP()))
|
|
{
|
|
for(pszTemp = (char *)pcszURL; *pszTemp; pszTemp = CharNext(pszTemp))
|
|
{
|
|
if (*pszTemp == chSlash || //for Unix
|
|
*pszTemp == chBSlash || //for UNCs
|
|
*pszTemp == chColon)
|
|
pszLeafFile = pszTemp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszLeafFile = (PSTR)&pcszURL[strlen(pcszURL) - 1];
|
|
|
|
for( ;
|
|
( *pszLeafFile != chSlash //for Unix
|
|
&& *pszLeafFile != chBSlash //for UNCs
|
|
&& *pszLeafFile != chColon);
|
|
pszLeafFile--)
|
|
{
|
|
XX_Assert(pszLeafFile > pcszURL, ("No leaf file name specified in URL!"));
|
|
}
|
|
}
|
|
#else
|
|
pszLeafFile = (PSTR)&pcszURL[strlen(pcszURL) - 1];
|
|
|
|
for( ;
|
|
( *pszLeafFile != chSlash //for Unix
|
|
&& *pszLeafFile != chBSlash //for UNCs
|
|
&& *pszLeafFile != chColon);
|
|
pszLeafFile--)
|
|
{
|
|
XX_Assert(pszLeafFile > pcszURL, ("No leaf file name specified in URL!"));
|
|
}
|
|
#endif
|
|
|
|
strncpy(pszFFn, pszLeafFile+1, iFFnLen );
|
|
pszFFn[_MAX_PATH] = 0;
|
|
|
|
#ifdef FEATURE_INTL // toupper() and islower() don't work with non-ascii characters
|
|
if (!IsFECodePage(GetACP())
|
|
|| (*pszFFn >= 0x61 && *pszFFn <= 0x7a))
|
|
#endif
|
|
*pszFFn = TOUPPER(*pszFFn);
|
|
|
|
if (IS_FLAG_CLEAR(dwFlags, FRIENDLYURL_FL_NO_HOST_PATH))
|
|
{
|
|
int adding_length;
|
|
char hostName[MAX_URL_STRING+1];
|
|
|
|
GetHostPathFromURL(pcszURL, hostName);
|
|
hostName[iFFnLen - MIN_URL_CHARS_IN_FF_NAME] = 0; // limit host name length
|
|
adding_length = strlen(hostName) + 2 + 2 + 1; // this is how much we propose to add
|
|
pszFFn[iFFnLen - adding_length] = 0; // make sure there's room to add
|
|
|
|
pszFFnT = pszFFn + strlen(pszFFn);
|
|
pszHostPathT = pszFFnT;
|
|
*pszFFnT++ = chSpace;
|
|
*pszFFnT++ = chLParen;
|
|
|
|
strcpy( pszFFnT, hostName );
|
|
pszFFnT += strlen(pszFFnT);
|
|
|
|
*pszFFnT++ = chRParen;
|
|
*pszFFnT = '\0';
|
|
/* Remove inval. filename chars. */
|
|
for (pszFFnT = pszFFn; ch = *pszFFnT;)
|
|
#ifdef FEATURE_INTL
|
|
if(IsDBCSLeadByte(ch))
|
|
pszFFnT += 2;
|
|
else
|
|
#endif
|
|
*pszFFnT++ = ChValidFilenameCh(ch);
|
|
|
|
if (pszHostPath)
|
|
strcpy(pszHostPath, pszHostPathT);
|
|
}
|
|
else
|
|
{
|
|
if (pszHostPath)
|
|
{
|
|
*pszHostPath = '\0';
|
|
WARNING_OUT(("GetFriendlyFilenameFromURL(): No host path requested."));
|
|
}
|
|
}
|
|
|
|
# undef pszParen
|
|
}
|
|
|
|
static void GetHostPathFromURL(PCSTR pcszURL, PSTR pszHostPath)
|
|
{
|
|
PSTR pszT;
|
|
#ifdef FEATURE_INTL
|
|
PSTR pszTemp;
|
|
#endif
|
|
|
|
if ( (pszT = HTParse(pcszURL, "", PARSE_HOST))
|
|
&& *pszT)
|
|
goto LGotHost;
|
|
|
|
if (pszT)
|
|
GTR_FREE(pszT);
|
|
|
|
if ( (pszT = HTParse(pcszURL, "", PARSE_PATH))
|
|
&& *pszT)
|
|
{
|
|
strcpy(pszHostPath, pszT);
|
|
goto LGotFullPath;
|
|
}
|
|
|
|
|
|
if (pszT)
|
|
GTR_FREE(pszT);
|
|
|
|
pszT = (PSTR)pcszURL;
|
|
for (; pszT && *pszT != chColon; pszT++);
|
|
if (!pszT)
|
|
{
|
|
strcpy(pszHostPath, pcszURL);
|
|
return;
|
|
}
|
|
|
|
strcpy(pszHostPath, ++pszT);
|
|
|
|
LGotFullPath:
|
|
/* Remove trailing filename */
|
|
#ifdef FEATURE_INTL
|
|
if (IsFECodePage(GetACP()))
|
|
{
|
|
for(pszT = pszTemp = pszHostPath; *pszTemp; pszTemp = CharNext(pszTemp))
|
|
{
|
|
if(*pszTemp == chSlash || //for Unix
|
|
*pszTemp == chBSlash) //for UNCs
|
|
pszT = pszTemp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszT = (PSTR)&pszHostPath[strlen(pszHostPath) - 1];
|
|
for(;
|
|
(pszT > pszHostPath
|
|
&& *pszT != chSlash //for Unix
|
|
&& *pszT != chBSlash); //for UNCs
|
|
pszT--)
|
|
;
|
|
}
|
|
#else
|
|
pszT = (PSTR)&pszHostPath[strlen(pszHostPath) - 1];
|
|
for(;
|
|
( pszT > pszHostPath
|
|
&& *pszT != chSlash //for Unix
|
|
&& *pszT != chBSlash); //for UNCs
|
|
pszT--)
|
|
;
|
|
#endif
|
|
*pszT = '\0';
|
|
return;
|
|
|
|
LGotHost:
|
|
strcpy(pszHostPath, pszT);
|
|
if (pszT)
|
|
GTR_FREE(pszT);
|
|
}
|
|
|
|
|
|
BOOL FExistsFile( PCSTR szFile,
|
|
BOOL fUpdateTime,
|
|
BY_HANDLE_FILE_INFORMATION *pbhfi)
|
|
{
|
|
HANDLE hFile;
|
|
FILETIME ft;
|
|
SYSTEMTIME st;
|
|
BOOL fExists;
|
|
|
|
hFile = CreateFile (szFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, //external viewer files are readonly
|
|
NULL);
|
|
if ( (fExists = (hFile != INVALID_HANDLE_VALUE))
|
|
&& (fUpdateTime))
|
|
{
|
|
GetSystemTime((LPSYSTEMTIME)&st);
|
|
SystemTimeToFileTime((LPSYSTEMTIME)&st, (LPFILETIME)&ft);
|
|
SetFileTime(hFile, NULL, NULL, (LPFILETIME)&ft);
|
|
}
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (pbhfi && !GetFileInformationByHandle(hFile, pbhfi))
|
|
fExists = FALSE;
|
|
CloseHandle(hFile);
|
|
}
|
|
return fExists;
|
|
}
|
|
|
|
BOOL FExistsDir( PCSTR szDir,
|
|
BOOL fCreate,
|
|
BOOL fErrorMsg)
|
|
{
|
|
DWORD dwFa;
|
|
|
|
if ( ((dwFa = GetFileAttributes(szDir)) != -1)
|
|
&& (dwFa & FILE_ATTRIBUTE_DIRECTORY))
|
|
return TRUE;
|
|
|
|
if(fCreate && FCreateScDir(szDir))
|
|
return TRUE;
|
|
|
|
if (fErrorMsg)
|
|
ERR_ReportError(NULL, errCantCreateShortcut, szDir, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static BOOL FCreateScDir(PCSTR szScDir)
|
|
{
|
|
char szDir[MAX_PATH+1];
|
|
char *pszDirEnd, *pszDirT;
|
|
|
|
strcpy(szDir, szScDir);
|
|
|
|
for(pszDirT = szDir, pszDirEnd = &szDir[strlen(szDir)];
|
|
pszDirT <= pszDirEnd;
|
|
#ifdef FEATURE_INTL
|
|
pszDirT = CharNext(pszDirT))
|
|
#else
|
|
pszDirT++)
|
|
#endif
|
|
{
|
|
if (*pszDirT == chBSlash || pszDirT == pszDirEnd)
|
|
{
|
|
*pszDirT = '\0';
|
|
if (FExistsDir(szDir, FALSE, FALSE))
|
|
goto LNextDir;
|
|
if (!CreateDirectory(szDir, NULL))
|
|
return FALSE;
|
|
LNextDir:
|
|
*pszDirT = chBSlash;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* pcszURL -> "ftp://ftp.microsoft.com"
|
|
* pcszPath -> "c:\windows\desktop\internet\Microsoft FTP.url"
|
|
*/
|
|
HRESULT CreateNewURLShortcut(PCSTR pcszURL, PCSTR pcszURLFile)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR wszURLFileUnicode[MAX_URL_STRING * 2];
|
|
|
|
if (MultiByteToWideChar(CP_ACP, 0, pcszURLFile, -1,
|
|
wszURLFileUnicode, MAX_URL_STRING * 2) > 0)
|
|
{
|
|
IUnknown *punk;
|
|
|
|
hr = SHCoCreateInstance(NULL, &CLSID_InternetShortcut,
|
|
NULL,
|
|
&IID_IUnknown,
|
|
&punk);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IUniformResourceLocator *purl;
|
|
|
|
hr = punk->lpVtbl->QueryInterface(punk, &IID_IUniformResourceLocator,
|
|
&purl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = purl->lpVtbl->SetURL(purl, pcszURL, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IPersistFile *ppf;
|
|
|
|
hr = punk->lpVtbl->QueryInterface(punk, &IID_IPersistFile, &ppf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ppf->lpVtbl->Save(ppf, wszURLFileUnicode, TRUE);
|
|
|
|
/* Don't need to QueryInterface for SaveCompleted
|
|
* because it's part of the IPersistFile implementation.
|
|
*/
|
|
if (SUCCEEDED(hr))
|
|
ppf->lpVtbl->SaveCompleted(ppf, wszURLFileUnicode); // return value always S_OK
|
|
|
|
ppf->lpVtbl->Release(ppf);
|
|
}
|
|
}
|
|
purl->lpVtbl->Release(purl);
|
|
}
|
|
punk->lpVtbl->Release(punk);
|
|
}
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/*
|
|
* pcszURLFile -> "c:\windows\desktop\internet\Microsoft FTP.url"
|
|
* pszURL -> "http://www.microsoft.com"
|
|
*/
|
|
BOOL FGetURLString( PCSTR pcszURLFile,
|
|
PSTR pszURL)
|
|
{
|
|
HRESULT hr;
|
|
IUnknown *punk;
|
|
WCHAR wszURLFileUnicode[MAX_URL_STRING * 2];
|
|
BOOL fRet=FALSE;
|
|
PSTR pszURLT;
|
|
|
|
if (MultiByteToWideChar(CP_ACP, 0, pcszURLFile, -1,
|
|
wszURLFileUnicode, MAX_URL_STRING * 2) <= 0)
|
|
return FALSE;
|
|
|
|
hr = SHCoCreateInstance(NULL, &CLSID_InternetShortcut,
|
|
NULL,
|
|
&IID_IUnknown,
|
|
&punk);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IPersistFile *ppf;
|
|
|
|
hr = punk->lpVtbl->QueryInterface(punk, &IID_IPersistFile, &ppf);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ppf->lpVtbl->Load(ppf, wszURLFileUnicode, TRUE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IUniformResourceLocator *purl;
|
|
|
|
hr = punk->lpVtbl->QueryInterface(punk, &IID_IUniformResourceLocator,
|
|
&purl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = purl->lpVtbl->GetURL(purl, &pszURLT);
|
|
if (SUCCEEDED(hr) && pszURLT)
|
|
{
|
|
fRet = TRUE;
|
|
strcpy(pszURL, pszURLT);
|
|
SHFree(pszURLT);
|
|
}
|
|
purl->lpVtbl->Release(purl);
|
|
}
|
|
}
|
|
ppf->lpVtbl->Release(ppf);
|
|
}
|
|
punk->lpVtbl->Release(punk);
|
|
}
|
|
|
|
if (!fRet)
|
|
ERR_ReportError(NULL, errInvalidURLShortcut, pcszURLFile, NULL);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL FExecExplorerAtShortcutsDir(UINT eeId, PCSTR pcszSubDir)
|
|
{
|
|
SHELLEXECUTEINFO ei;
|
|
char szDirHot[MAX_PATH+1];
|
|
char szDirHist[MAX_PATH+1];
|
|
char *pszSubDir=NULL;
|
|
|
|
XX_Assert(eeId == ID_HISTORY || eeId == ID_HOTLIST || eeId == ID_SUBDIR, ("Illegal ID: expect ID_HOTLIST/HISTORY/SUBDIR!"));
|
|
|
|
if (GetInternetScDir(szDirHot, ID_HOTLIST) != S_OK && eeId == ID_HOTLIST)
|
|
return FALSE;
|
|
|
|
if ( ( GetInternetScDir(szDirHist, ID_HISTORY) != S_OK
|
|
|| !FExistsDir(szDirHist, /*fCreate=*/TRUE, /*fErrorMsg=*/TRUE))
|
|
&& (eeId == ID_HISTORY))
|
|
return FALSE;
|
|
|
|
/* The subdir must be somewhere in the history/hotlist directory */
|
|
switch (eeId)
|
|
{
|
|
case ID_SUBDIR:
|
|
{
|
|
XX_Assert(pcszSubDir, ("SubDir NULL for ID_SUBDIR case!"));
|
|
|
|
if ( strstr(pcszSubDir, szDirHot) == pcszSubDir
|
|
|| strstr(pcszSubDir, szDirHist) == pcszSubDir)
|
|
pszSubDir = (PSTR)pcszSubDir;
|
|
else
|
|
{
|
|
XX_Assert(0, ("Subdir is not in the history/hotlist dirs"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!FExistsDir(pcszSubDir, FALSE, FALSE))
|
|
{
|
|
XX_Assert(0, ("Couldn't find directory %s!", pcszSubDir));
|
|
return FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case ID_HISTORY:
|
|
pszSubDir = (PSTR)szDirHist;
|
|
break;
|
|
case ID_HOTLIST:
|
|
pszSubDir = (PSTR)szDirHot;
|
|
break;
|
|
}
|
|
|
|
ei.cbSize = sizeof(ei);
|
|
ei.hwnd = wg.hWndHidden;
|
|
ei.lpVerb = NULL;
|
|
ei.fMask = 0;
|
|
ei.lpFile = pszSubDir;
|
|
ei.lpParameters = NULL;
|
|
ei.lpDirectory = NULL;
|
|
ei.lpClass = NULL;
|
|
ei.nShow = SW_SHOWDEFAULT;
|
|
return(ShellExecuteEx(&ei));
|
|
}
|
|
|
|
|
|
/*
|
|
* GetShellFolderPath()
|
|
*
|
|
* Retrieves path to Shell folder.
|
|
*
|
|
* hwndOwner - handle to parent window
|
|
* nFolder - Shell folder whose path is to be retrieved, Shell folders are
|
|
* listed in shlobj.h, e.g., you can get the Desktop folder path
|
|
* using nFolder == CSIDL_DESKTOP
|
|
* szPath - string buffer to be filled in with path to Shell folder, assumed to
|
|
* be at least MAX_PATH_LEN bytes in length
|
|
*
|
|
* Success: S_OK
|
|
*
|
|
* Failure: E_INVALIDARG
|
|
* E_OUTOFMEMORY
|
|
*/
|
|
HRESULT GetShellFolderPath(HWND hwndOwner, int nFolder, PSTR szPath)
|
|
{
|
|
HRESULT hr;
|
|
LPITEMIDLIST pidl;
|
|
|
|
hr = SHGetSpecialFolderLocation(hwndOwner, nFolder, &pidl);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (! SHGetPathFromIDList(pidl, szPath))
|
|
hr = E_INVALIDARG;
|
|
|
|
SHFree(pidl);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|