home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
ncftp-2.3.0-src.tgz
/
tar.out
/
contrib
/
ncftp
/
Bookmark.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-09-28
|
19KB
|
831 lines
/* Bookmark.c */
#include "Sys.h"
#include <ctype.h>
/* TO-DO: If I decide to implement strstr matching, watch out for
* local domain hosts, with simple names.
*/
#include "Util.h"
#include "Bookmark.h"
#include "FTP.h"
/* We keep an array of structures of all the sites we know about in
* a contigious block of memory.
*/
BookmarkPtr gHosts = (BookmarkPtr) 0;
/* But we also keep an array of pointers to the structures, so we can
* use that for sorting purposes. We don't want to have to shuffle
* large structures around when sorting. As the name hints, we have
* this array sorted by nickname, for quick searching.
*/
BookmarkPtrList gBookmarks = (BookmarkPtrList) 0;
/* This is the number of elements in both gHosts and gBookmarks. */
int gNumBookmarks = 0;
/* We don't want the other portions of the program overwriting the
* current entry in the gHosts list, because we may want to save our
* changes under a new entry. So if the host was in our list, we make
* a copy of the data, and let them write into that. Then we can write
* the changed structure under a new entry, or overwrite the old one.
* This also works for entirely new entries. We just give the caller
* this, initialized to the default values.
*/
Bookmark gRmtInfo = { NULL, NULL, 0, "", "" };
/* Used to tell if we got the information from the host information list,
* or we were using a new entry.
*/
int gRmtInfoIsNew;
/* We use this outside of this module to tell if we should actually
* save the information collected. We don't want to save it if the
* stuff wasn't really valid, so we won't save unless you logged in
* successfully.
*/
int gWantRmtInfoSaved;
/* Used to tell if the host file needs to be written back out.
* If we haven't changed anything, then don't waste the time to
* write the file.
*/
int gModifiedBookmarks = 0;
/* These are the first and last nodes in the linked-list of remote
* site information structures.
*/
BookmarkPtr gFirstRsi = NULL, gLastRsi = NULL;
/* If greater than zero, we will only save the most recent sites, up
* to this number.
*/
int gMaxBookmarks = kNoBookmarkLimit;
extern string gEmailAddress, gAnonPassword;
extern int gPreferredDataPortMode;
extern string gOurDirectoryPath;
extern longstring gRemoteCWD;
static
int BookmarkSortProc(const BookmarkPtr *a, const BookmarkPtr *b)
{
return (ISTRCMP((**a).bookmarkName, (**b).bookmarkName));
} /* BookmarkSortProc */
static
int BookmarkSortTimeProc(const BookmarkPtr *a, const BookmarkPtr *b)
{
return ((**b).lastCall - (**a).lastCall);
} /* BookmarkSortTimeProc */
static
int BookmarkSearchProc(char *key, const BookmarkPtr *b)
{
return (ISTRCMP(key, (**b).bookmarkName));
} /* BookmarkSearchProc */
void SortBookmarks(void)
{
int i;
BookmarkPtr p;
if (gBookmarks != (BookmarkPtrList) 0)
free(gBookmarks);
gBookmarks = (BookmarkPtrList) malloc(
sizeof(BookmarkPtr) * (gNumBookmarks + 1)
);
if (gBookmarks == (BookmarkPtrList) 0)
OutOfMemory();
for (p = gFirstRsi, i=0; p != NULL; i++, p = p->next) {
gBookmarks[i] = p;
}
gBookmarks[gNumBookmarks] = NULL;
QSORT(gBookmarks,
gNumBookmarks, sizeof(BookmarkPtr), BookmarkSortProc);
for (i=0; i<gNumBookmarks; i++) {
p = gBookmarks[i];
p->index = i;
}
} /* SortBookmarks */
void UpdateBookmarkPtr(BookmarkPtr dst, BookmarkPtr src)
{
BookmarkPtr next, prev;
int idx;
/* Need to preserve dst's links, but copy all of src's stuff. */
next = dst->next;
prev = dst->prev;
idx = dst->index;
*dst = *src;
dst->next = next;
dst->prev = prev;
dst->index = idx;
} /* UpdateBookmarkPtr */
BookmarkPtr AddBookmarkPtr(BookmarkPtr buf)
{
BookmarkPtr newRsip;
newRsip = (BookmarkPtr) malloc(sizeof(Bookmark));
if (newRsip != NULL) {
memcpy(newRsip, buf, sizeof(Bookmark));
newRsip->next = NULL;
if (gFirstRsi == NULL) {
gFirstRsi = gLastRsi = newRsip;
newRsip->prev = NULL;
} else {
newRsip->prev = gLastRsi;
gLastRsi->next = newRsip;
gLastRsi = newRsip;
}
++gNumBookmarks;
/* Just need to know if we should write out the host file later. */
gModifiedBookmarks++;
} else {
OutOfMemory();
}
return newRsip;
} /* AddBookmarkPtr */
BookmarkPtr RemoveBookmarkPtr(BookmarkPtr killMe)
{
BookmarkPtr nextRsi, prevRsi;
nextRsi = killMe->next;
prevRsi = killMe->prev;
if (gFirstRsi == killMe)
gFirstRsi = nextRsi;
if (gLastRsi == killMe)
gLastRsi = prevRsi;
if (nextRsi != NULL)
nextRsi->prev = prevRsi;
if (prevRsi != NULL)
prevRsi->next = nextRsi;
PTRZERO(killMe, sizeof(Bookmark));
free(killMe);
--gNumBookmarks;
++gModifiedBookmarks;
return (nextRsi);
} /* RemoveBookmarkPtr */
void MakeBookmarkUnique(char *dst, size_t siz)
{
int i;
string s2, s3;
BookmarkPtr *bmpp;
char *cp;
/* Make sure we can concat 3 more characters if necessary. */
Strncpy(s2, dst, siz - 3);
for (cp = s2 + strlen(s2) - 1; cp > s2; ) {
if (isdigit(*cp))
*cp-- = '\0';
else
break;
}
/* Make a copy of the original. */
STRNCPY(s3, dst);
for (i=1; i<=999; i++) {
if (i > 1)
sprintf(dst, "%s%d", s2, i);
else
Strncpy(dst, s3, siz);
/* See if there is already a nickname by this name. */
if (gNumBookmarks == 0)
break;
bmpp = (BookmarkPtr *) BSEARCH(dst, gBookmarks, gNumBookmarks,
sizeof(BookmarkPtr), BookmarkSearchProc);
if (bmpp == NULL)
break;
}
} /* MakeBookmarkUnique */
void MakeUpABookmarkName(char *dst, size_t siz, char *src)
{
string str;
char *token;
char *cp;
STRNCPY(str, src);
/* Pick the first "significant" part of the hostname. Usually
* this is the first word in the name, but if it's something like
* ftp.unl.edu, we would want to choose "unl" and not "ftp."
*/
token = str;
if ((token = strtok(token, ".")) == NULL)
token = "misc";
else if (ISTREQ(token, "ftp")) {
if ((token = strtok(NULL, ".")) == NULL)
token = "misc";
}
for (cp = token; ; cp++) {
if (*cp == '\0') {
/* Token was all digits, like an IP address perhaps. */
token = "misc";
}
if (!isdigit(*cp))
break;
}
Strncpy(dst, token, siz);
MakeBookmarkUnique(dst, siz);
} /* MakeUpABookmarkName */
void SetBookmarkDefaults(BookmarkPtr bmp)
{
PTRZERO(bmp, sizeof(Bookmark));
bmp->xferType = 'I';
bmp->port = kPortUnset;
bmp->hasSIZE = 1;
bmp->hasMDTM = 1;
if (gPreferredDataPortMode >= kPassiveMode) {
/* Assume we have it until proven otherwise. */
bmp->hasPASV = 1;
} else {
/* If default is PORT, then make the user explicitly set this. */
bmp->hasPASV = 0;
}
bmp->isUnix = 1;
bmp->lastCall = (time_t) 0;
} /* SetBookmarkDefaults */
void SetNewBookmarkDefaults(BookmarkPtr bmp)
{
/* Return a pointer to a new entry, initialized to
* all the defaults, except for name and nickname.
*/
SetBookmarkDefaults(bmp);
STRNCPY(bmp->name, "foobar.snafu.gov");
STRNCPY(bmp->bookmarkName, "NEW");
/* That will make a unique "NEW" nickname. */
MakeBookmarkUnique(bmp->bookmarkName, sizeof(bmp->bookmarkName));
} /* SetNewBookmarkDefaults */
int GetBookmark(char *host, size_t siz)
{
BookmarkPtr *bmpp;
int i;
size_t len;
if (gNumBookmarks == 0)
bmpp = NULL;
else {
bmpp = (BookmarkPtr *)
BSEARCH(host, gBookmarks, gNumBookmarks,
sizeof(BookmarkPtr), BookmarkSearchProc);
if (bmpp == NULL) {
/* No exact match, but the user doesn't have to type the
* whole nickname, just the first few letters of it.
*/
/* This could probably be done in a bsearch proc too... */
len = strlen(host);
for (i=0; i<gNumBookmarks; i++) {
if (ISTRNEQ(gBookmarks[i]->bookmarkName, host, len)) {
bmpp = &gBookmarks[i];
break;
}
}
}
if ((bmpp == NULL) && (strchr(host, '.') != NULL)) {
/* If thing we were given looks like a full hostname (has at
* least one period), see if we have an exact match on the
* hostname.
*
* This is actually not recommended -- you should try to use
* the nicknames only since they are unique. We can have more
* than one entry for the same hostname!
*/
for (i=0; i<gNumBookmarks; i++) {
if (ISTREQ(gBookmarks[i]->name, host)) {
bmpp = &gBookmarks[i];
break;
}
}
}
}
gWantRmtInfoSaved = 0;
if (bmpp != NULL) {
gRmtInfo = **bmpp;
/* So we know that this isn't in the list, but just a copy
* of someone else's data.
*/
gRmtInfo.next = gRmtInfo.prev = NULL;
gRmtInfoIsNew = 0;
/* gHost needs to be set here, since the caller wasn't using
* a real host name.
*/
Strncpy(host, gRmtInfo.name, siz);
return (1);
}
SetNewBookmarkDefaults(&gRmtInfo);
STRNCPY(gRmtInfo.name, host);
MakeUpABookmarkName(gRmtInfo.bookmarkName, sizeof(gRmtInfo.bookmarkName), host);
gRmtInfoIsNew = 1;
return (0);
} /* GetBookmark */
int ParseHostLine(char *line, BookmarkPtr bmp)
{
string token;
char *s, *d;
char *tokenend;
long L;
int i;
int result;
SetBookmarkDefaults(bmp);
s = line;
tokenend = token + sizeof(token) - 1;
result = -1;
for (i=0; ; i++) {
if (*s == '\0')
break;
/* Some tokens may need to have a comma in them. Since this is a
* field delimiter, these fields use \, to represent a comma, and
* \\ for a backslash. This chunk gets the next token, paying
* attention to the escaped stuff.
*/
for (d = token; *s != '\0'; ) {
if ((*s == '\\') && (s[1] != '\0')) {
if (d < tokenend)
*d++ = s[1];
s += 2;
} else if (*s == ',') {
++s;
break;
} else {
if (d < tokenend)
*d++ = *s;
++s;
}
}
*d = '\0';
switch(i) {
case 0: (void) STRNCPY(bmp->bookmarkName, token); break;
case 1: (void) STRNCPY(bmp->name, token); break;
case 2: (void) STRNCPY(bmp->user, token); break;
case 3: (void) STRNCPY(bmp->pass, token); break;
case 4: (void) STRNCPY(bmp->acct, token); break;
case 5: (void) STRNCPY(bmp->dir, token);
result = 0; /* Good enough to have these fields. */
break;
case 6: bmp->xferType = token[0]; break;
case 7:
/* Most of the time, we won't have a port. */
if (token[0] == '\0')
bmp->port = (unsigned int) kDefaultFTPPort;
else
bmp->port = (unsigned int) atoi(token);
break;
case 8:
sscanf(token, "%lx", &L);
bmp->lastCall = (time_t) L;
break;
case 9: bmp->hasSIZE = atoi(token); break;
case 10: bmp->hasMDTM = atoi(token); break;
case 11: bmp->hasPASV = atoi(token); break;
case 12: bmp->isUnix = atoi(token);
result = 3; /* Version 3 had all fields to here. */
break;
case 13: (void) STRNCPY(bmp->lastIP, token); break;
case 14: (void) STRNCPY(bmp->comment, token); break;
case 15: sscanf(token, "%ld", &bmp->xferKbytes); break;
case 16: sscanf(token, "%ld", &bmp->xferHSeconds);
result = 4; /* Version 4 had all fields up to here. */
break;
case 17: bmp->nCalls = atoi(token);
result = 5; /* Version 5 has all fields to here. */
break;
case 18: bmp->noSaveDir = atoi(token);
result = 6; /* Version 5 has all fields to here. */
break;
default:
result = 99; /* Version >4 ? */
goto done;
}
}
done:
return (result);
} /* ParseHostLine */
void ReadBookmarkFile(void)
{
string pathName;
string path2;
FILE *fp;
longstring line;
int version;
Bookmark newRsi;
if (gOurDirectoryPath[0] == '\0')
return; /* Don't create in root directory. */
OurDirectoryPath(pathName, sizeof(pathName), kBookmarkFileName);
fp = fopen(pathName, "r");
if (fp == NULL) {
OurDirectoryPath(path2, sizeof(path2), kOldBookmarkFileName);
if (rename(path2, pathName) == 0) {
/* Rename succeeded, now open it. */
fp = fopen(pathName, "r");
if (fp == NULL)
return;
}
return; /* Okay to not have one yet. */
}
if (FGets(line, sizeof(line), fp) == NULL)
goto badFmt;
/* Sample line we're looking for:
* "NcFTP bookmark-file version: 2"
*/
version = -1;
(void) sscanf(line, "%*s %*s %*s %d", &version);
if (version < kBookmarkMinVersion) {
if (version < 0)
goto badFmt;
STRNCPY(path2, pathName);
sprintf(line, ".v%d", version);
STRNCAT(path2, line);
(void) rename(pathName, path2);
Error(kDontPerror, "%s: old version.\n", pathName);
fclose(fp);
return;
}
if (FGets(line, sizeof(line), fp) == NULL)
goto badFmt;
/* Sample line we're looking for:
* "Number of entries: 28"
*/
gNumBookmarks = -1;
/* At the moment, we don't really care about the number stored in the
* file. It's there for future use.
*/
(void) sscanf(line, "%*s %*s %*s %d", &gNumBookmarks);
if (gNumBookmarks < 0)
goto badFmt;
gHosts = (BookmarkPtr) 0;
gBookmarks = (BookmarkPtrList) 0;
gNumBookmarks = 0;
while (FGets(line, sizeof(line), fp) != NULL) {
if (ParseHostLine(line, &newRsi) >= 0) {
AddBookmarkPtr(&newRsi);
}
}
fclose(fp);
SortBookmarks();
DebugMsg("Read %d entries from %s.\n", gNumBookmarks, pathName);
return;
badFmt:
Error(kDontPerror, "%s: invalid format.\n", pathName);
fclose(fp);
} /* ReadBookmarkFile */
BookmarkPtr DuplicateBookmark(BookmarkPtr origbmp)
{
Bookmark newRsi;
BookmarkPtr newRsip;
string str;
STRNCPY(str, origbmp->bookmarkName);
MakeBookmarkUnique(str, sizeof(origbmp->bookmarkName));
newRsi = *origbmp;
STRNCPY(newRsi.bookmarkName, str);
newRsip = AddBookmarkPtr(&newRsi);
/* Have to re-sort now so our bsearches will work. */
SortBookmarks();
return (newRsip);
} /* DuplicateBookmark */
void DeleteBookmark(BookmarkPtr bmp)
{
if (gNumBookmarks < 1)
return;
RemoveBookmarkPtr(bmp);
SortBookmarks();
} /* DuplicateBookmark */
void SaveBookmark(char *asNick)
{
BookmarkPtr *bmpp;
Bookmark rm;
memcpy(&rm, &gRmtInfo, sizeof(rm));
STRNCPY(rm.bookmarkName, asNick);
STRNCPY(rm.dir, gRemoteCWD);
rm.xferKbytes = 0L;
rm.xferHSeconds = 0L;
rm.nCalls = 0;
/* Don't update dir if you move around the next time you use it. */
rm.noSaveDir = 1;
bmpp = (BookmarkPtr *) BSEARCH(asNick,
gBookmarks, gNumBookmarks,
sizeof(BookmarkPtr), BookmarkSearchProc);
if (bmpp == NULL) {
/* Add a new entry. */
rm.comment[0] = '\0';
(void) AddBookmarkPtr(&rm);
/* Have to re-sort now so our bsearches will work. */
SortBookmarks();
PrintF("Saving new bookmark named \"%s\" in your host file, pointing\nto <URL:ftp://%s/%s/>.\n",
rm.bookmarkName,
rm.name,
rm.dir
);
} else {
/* Copy over an existing one. */
UpdateBookmarkPtr(*bmpp, &rm);
PrintF("Updated bookmark named \"%s\" in your host file, so it now points\nto <URL:ftp://%s/%s/>.\n",
rm.bookmarkName,
rm.name,
rm.dir
);
}
/* Just need to know if we should write out the host file later. */
gModifiedBookmarks++;
} /* SaveBookmark */
void SaveCurHostBookmark(char *asNick)
{
BookmarkPtr *bmpp;
if (gRmtInfoIsNew) {
(void) AddBookmarkPtr(&gRmtInfo);
PrintF("Saving new bookmark named \"%s\" in your host file, pointing\nto <URL:ftp://%s/%s/>.\n",
gRmtInfo.bookmarkName,
gRmtInfo.name,
gRmtInfo.dir
);
/* Have to re-sort now so our bsearches will work. */
SortBookmarks();
} else {
/* We were working with an existing entry.
* If the nickname given to us as the parameter is different
* from the existing bookmarkName, then we're supposed to save
* this as a new entry.
*/
if ((asNick == NULL) || ISTREQ(asNick, gRmtInfo.bookmarkName)) {
/* Save over old entry. */
bmpp = (BookmarkPtr *) BSEARCH(gRmtInfo.bookmarkName,
gBookmarks, gNumBookmarks,
sizeof(BookmarkPtr), BookmarkSearchProc);
/* This had better be in there, since we did this before
* and it was in there.
*/
if (bmpp == NULL) {
Error(kDontPerror,
"Programmer's error: couldn't re-find host info entry.\n");
return;
}
/* Copy over the old stuff. */
UpdateBookmarkPtr(*bmpp, &gRmtInfo);
/* Just need to know if we should write out the host file later. */
gModifiedBookmarks++;
} else {
/* Add a new entry. */
STRNCPY(gRmtInfo.bookmarkName, asNick);
MakeBookmarkUnique(gRmtInfo.bookmarkName, sizeof(gRmtInfo.bookmarkName));
(void) AddBookmarkPtr(&gRmtInfo);
/* Have to re-sort now so our bsearches will work. */
SortBookmarks();
}
}
gRmtInfoIsNew = 0;
} /* SaveCurHostBookmark */
static
void EscapeString(char *d, char *s)
{
if (s != NULL) {
while (*s != '\0') {
if (*s == ',' || *s == '\\')
*d++ = '\\';
*d++ = *s++;
}
}
*d = '\0';
} /* EscapeString */
void WriteBookmarkFile(void)
{
string pathName;
string bupPathName;
longstring escapedStr;
FILE *fp;
char portStr[16];
int i;
int nPasswds;
BookmarkPtr bmp;
if (!gModifiedBookmarks)
return;
OurDirectoryPath(pathName, sizeof(pathName), kBookmarkFileName);
if ((gMaxBookmarks != kNoBookmarkLimit) && (gNumBookmarks > gMaxBookmarks)) {
DebugMsg("Purging %d old remote sites from %s.\n",
gNumBookmarks - gMaxBookmarks,
pathName
);
/* Sort sites by last time we called. We want the older sites to
* float to the bottom.
*/
QSORT(gBookmarks,
gNumBookmarks, sizeof(BookmarkPtr), BookmarkSortTimeProc);
gNumBookmarks = gMaxBookmarks;
}
/* See if we can move the existing file to a new name, in case
* something happens while we write this out. Host files are
* valuable enough that people would be pissed off if their
* host file got nuked.
*/
OurDirectoryPath(bupPathName, sizeof(bupPathName), kBookmarkBupFileName);
(void) UNLINK(bupPathName);
(void) rename(pathName, bupPathName);
fp = fopen(pathName, "w");
if (fp == NULL)
goto err;
if (fprintf(fp, "NcFTP bookmark-file version: %d\nNumber of entries: %d\n",
kBookmarkVersion,
gNumBookmarks
) < 0)
goto err;
if (fflush(fp) < 0)
goto err;
for (i=0, nPasswds=0; i<gNumBookmarks; i++) {
*portStr = '\0';
bmp = gBookmarks[i];
if (bmp->port != kDefaultFTPPort)
sprintf(portStr, "%u", bmp->port);
if ((bmp->pass[0] != '\0')
&& (!STREQ(bmp->pass, gEmailAddress))
&& (!STREQ(bmp->pass, gAnonPassword)))
nPasswds++;
if (bmp->acct[0] != '\0')
nPasswds++; /* Don't publicize accounts, either. */
/* Insert the quote character '\' for strings that can have
* commas or backslashes in them.
*/
EscapeString(escapedStr, bmp->pass);
if (fprintf(fp, "%s,%s,%s,%s,%s,",
bmp->bookmarkName,
bmp->name,
bmp->user,
escapedStr,
bmp->acct
) < 0)
goto err;
EscapeString(escapedStr, bmp->dir);
if (fprintf(fp, "%s,%c,%s,%lx,%d,%d,%d,%d,",
escapedStr,
bmp->xferType,
portStr,
(unsigned long) bmp->lastCall,
bmp->hasSIZE,
bmp->hasMDTM,
bmp->hasPASV,
bmp->isUnix
) < 0)
goto err;
EscapeString(escapedStr, bmp->comment);
if (fprintf(fp, "%s,%s,%ld,%ld,%d,%d\n",
bmp->lastIP,
escapedStr,
bmp->xferKbytes,
bmp->xferHSeconds,
bmp->nCalls,
bmp->noSaveDir
) < 0)
goto err;
}
if (fclose(fp) < 0) {
fp = NULL;
goto err;
}
(void) UNLINK(bupPathName);
if (nPasswds > 0) {
/* Set permissions so other users can't see the passwords.
* Of course this isn't really secure, which is why the program
* won't save passwords entered at the password prompt. You must
* explicitly set them from the host editor.
*/
(void) chmod(pathName, 0600); /* Set it to -rw------- */
}
return;
err:
if (access(bupPathName, F_OK) < 0) {
Error(kDoPerror, "Could not write to %s.\n", pathName);
} else {
/* Move backup file back to the original. */
rename(bupPathName, pathName);
Error(kDoPerror, "Could not update %s.\n", pathName);
}
if (fp != NULL)
fclose(fp);
} /* WriteBookmarkFile */