📄 bookmark.c
字号:
/* bookmark.c
*
* Copyright (c) 1992-2001 by Mike Gleason.
* All rights reserved.
*
*/
#include "syshdrs.h"
#include "bookmark.h"
#include "util.h"
/*
* The ~/.ncftp/bookmarks file contains a list of sites
* the user wants to remember.
*
* Unlike previous versions of the program, we now open/close
* the file every time we need it; That way we can have
* multiple ncftp processes changing the file. There is still
* a possibility that two different processes could be modifying
* the file at the same time.
*/
Bookmark gBm;
int gLoadedBm = 0;
int gBookmarkMatchMode = 0;
int gNumBookmarks = 0;
BookmarkPtr gBookmarkTable = NULL;
extern char gOurDirectoryPath[];
/* Converts a pre-loaded Bookmark structure into a RFC 1738
* Uniform Resource Locator.
*/
void
BookmarkToURL(BookmarkPtr bmp, char *url, size_t urlsize)
{
char pbuf[32];
/* //<user>:<password>@<host>:<port>/<url-path> */
/* Note that if an absolute path is given,
* you need to escape the first entry, i.e. /pub -> %2Fpub
*/
(void) Strncpy(url, "ftp://", urlsize);
if (bmp->user[0] != '\0') {
(void) Strncat(url, bmp->user, urlsize);
if (bmp->pass[0] != '\0') {
(void) Strncat(url, ":", urlsize);
(void) Strncat(url, "PASSWORD", urlsize);
}
(void) Strncat(url, "@", urlsize);
}
(void) Strncat(url, bmp->name, urlsize);
if (bmp->port != 21) {
(void) sprintf(pbuf, ":%u", (unsigned int) bmp->port);
(void) Strncat(url, pbuf, urlsize);
}
if (bmp->dir[0] == '/') {
/* Absolute URL path, must escape first slash. */
(void) Strncat(url, "/%2F", urlsize);
(void) Strncat(url, bmp->dir + 1, urlsize);
(void) Strncat(url, "/", urlsize);
} else if (bmp->dir[0] != '\0') {
(void) Strncat(url, "/", urlsize);
(void) Strncat(url, bmp->dir, urlsize);
(void) Strncat(url, "/", urlsize);
}
} /* BookmarkToURL */
void
SetBookmarkDefaults(BookmarkPtr bmp)
{
(void) memset(bmp, 0, sizeof(Bookmark));
bmp->xferType = 'I';
bmp->xferMode = 'S'; /* Use FTP protocol default as ours too. */
bmp->hasSIZE = kCommandAvailabilityUnknown;
bmp->hasMDTM = kCommandAvailabilityUnknown;
bmp->hasUTIME = kCommandAvailabilityUnknown;
bmp->hasPASV = kCommandAvailabilityUnknown;
bmp->isUnix = 1;
bmp->lastCall = (time_t) 0;
bmp->deleted = 0;
} /* SetBookmarkDefaults */
/* Used when converting hex strings to integral types. */
static int
HexCharToNibble(int c)
{
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (c - '0');
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
return (c - 'a' + 10);
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return (c - 'A' + 10);
}
return (-1); /* Error. */
} /* HexCharToNibble */
/* Fills in a Bookmark structure based off of a line from the NcFTP
* "bookmarks" file.
*/
int
ParseHostLine(char *line, BookmarkPtr bmp)
{
char token[128];
char pass[128];
char *s, *d;
char *tokenend;
long L;
int i;
int result;
int n, n1, n2;
SetBookmarkDefaults(bmp);
s = line;
tokenend = token + sizeof(token) - 1;
result = -1;
for (i=1; ; 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 ((*s == '$') && (s[1] != '\0') && (s[2] != '\0')) {
n1 = HexCharToNibble(s[1]);
n2 = HexCharToNibble(s[2]);
if ((n1 >= 0) && (n2 >= 0)) {
n = (n1 << 4) | n2;
if (d < tokenend)
*(unsigned char *)d++ = (unsigned int) n;
}
s += 3;
} else {
if (d < tokenend)
*d++ = *s;
++s;
}
}
*d = '\0';
switch(i) {
case 1: (void) STRNCPY(bmp->bookmarkName, token); break;
case 2: (void) STRNCPY(bmp->name, token); break;
case 3: (void) STRNCPY(bmp->user, token); break;
case 4: (void) STRNCPY(bmp->pass, token); break;
case 5: (void) STRNCPY(bmp->acct, token); break;
case 6: (void) STRNCPY(bmp->dir, token);
result = 0; /* Good enough to have these fields. */
break;
case 7:
if (token[0] != '\0')
bmp->xferType = (int) token[0];
break;
case 8:
/* 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 9:
(void) sscanf(token, "%lx", &L);
bmp->lastCall = (time_t) L;
break;
case 10: bmp->hasSIZE = atoi(token); break;
case 11: bmp->hasMDTM = atoi(token); break;
case 12: bmp->hasPASV = atoi(token); break;
case 13: bmp->isUnix = atoi(token);
result = 3; /* Version 3 had all fields to here. */
break;
case 14: (void) STRNCPY(bmp->lastIP, token); break;
case 15: (void) STRNCPY(bmp->comment, token); break;
case 16:
case 17:
case 18:
case 19:
break;
case 20: bmp->xferMode = token[0];
result = 7; /* Version 7 has all fields to here. */
break;
case 21: bmp->hasUTIME = atoi(token);
break;
case 22: (void) STRNCPY(bmp->ldir, token);
result = 8; /* Version 8 has all fields to here. */
break;
default:
result = 99; /* Version >8 ? */
goto done;
}
}
done:
/* Decode password, if it was base-64 encoded. */
if (strncmp(bmp->pass, kPasswordMagic, kPasswordMagicLen) == 0) {
FromBase64(pass, bmp->pass + kPasswordMagicLen, strlen(bmp->pass + kPasswordMagicLen), 1);
(void) STRNCPY(bmp->pass, pass);
}
return (result);
} /* ParseHostLine */
void
CloseBookmarkFile(FILE *fp)
{
if (fp != NULL)
(void) fclose(fp);
} /* CloseBookmarkFile */
int
GetNextBookmark(FILE *fp, Bookmark *bmp)
{
char line[512];
while (FGets(line, sizeof(line), fp) != NULL) {
if (ParseHostLine(line, bmp) >= 0)
return (0);
}
return (-1);
} /* GetNextBookmark */
/* Opens a NcFTP 2.x or 3.x style bookmarks file, and sets the file pointer
* so that it is ready to read the first data line.
*/
FILE *
OpenBookmarkFile(int *numBookmarks0)
{
char pathName[256], path2[256];
char line[256];
FILE *fp;
int version;
int numBookmarks;
Bookmark junkbm;
if (gOurDirectoryPath[0] == '\0')
return NULL; /* Don't create in root directory. */
(void) OurDirectoryPath(pathName, sizeof(pathName), kBookmarkFileName);
fp = fopen(pathName, FOPEN_READ_TEXT);
if (fp == NULL) {
/* See if it exists under the old name. */
(void) OurDirectoryPath(path2, sizeof(path2), kOldBookmarkFileName);
if (rename(path2, pathName) == 0) {
/* Rename succeeded, now open it. */
fp = fopen(pathName, FOPEN_READ_TEXT);
if (fp == NULL)
return NULL;
}
return NULL; /* Okay to not have one yet. */
}
(void) chmod(pathName, 00600);
if (FGets(line, sizeof(line), fp) == NULL) {
(void) fprintf(stderr, "%s: invalid format.\n", pathName);
(void) fclose(fp);
return NULL;
}
/* Sample line we're looking for:
* "NcFTP bookmark-file version: 8"
*/
version = -1;
(void) sscanf(line, "%*s %*s %*s %d", &version);
if (version < kBookmarkMinVersion) {
if (version < 0) {
(void) fprintf(stderr, "%s: invalid format, or bad version.\n", pathName);
(void) fclose(fp);
return NULL;
}
(void) STRNCPY(path2, pathName);
(void) sprintf(line, ".v%d", version);
(void) STRNCAT(path2, line);
(void) rename(pathName, path2);
(void) fprintf(stderr, "%s: old version.\n", pathName);
(void) fclose(fp);
return NULL;
}
/* Sample line we're looking for:
* "Number of entries: 28" or "# # # 1"
*/
numBookmarks = -1;
/* At the moment, we can't trust the number stored in the
* file. It's there for future use.
*/
if (FGets(line, sizeof(line), fp) == NULL) {
(void) fprintf(stderr, "%s: invalid format.\n", pathName);
(void) fclose(fp);
return NULL;
}
if (numBookmarks0 == (int *) 0) {
/* If the caller doesn't care how many bookmarks are *really*
* in the file, then we can return now.
*/
return(fp);
}
/* Otherwise, we have to read through the whole file because
* unfortunately the header line can't be trusted.
*/
for (numBookmarks = 0; ; numBookmarks++) {
if (GetNextBookmark(fp, &junkbm) < 0)
break;
}
/* Now we have to re-open and re-position the file.
* We don't use rewind() because it doesn't always work.
* This introduces a race condition, but the bookmark
* functionality wasn't designed to be air-tight.
*/
CloseBookmarkFile(fp);
fp = fopen(pathName, FOPEN_READ_TEXT);
if (fp == NULL)
return (NULL);
if (FGets(line, sizeof(line), fp) == NULL) {
(void) fprintf(stderr, "%s: invalid format.\n", pathName);
(void) fclose(fp);
return NULL;
}
if (FGets(line, sizeof(line), fp) == NULL) {
(void) fprintf(stderr, "%s: invalid format.\n", pathName);
(void) fclose(fp);
return NULL;
}
/* NOW we're done. */
*numBookmarks0 = numBookmarks;
return (fp);
} /* OpenBookmarkFile */
/* Looks for a saved bookmark by the abbreviation given. */
int
GetBookmark(const char *const bmabbr, Bookmark *bmp)
{
FILE *fp;
char line[512];
Bookmark byHostName;
Bookmark byHostAbbr;
Bookmark byBmAbbr;
size_t byBmNameFlag = 0;
size_t byBmAbbrFlag = 0;
size_t byHostNameFlag = 0;
size_t byHostAbbrFlag = 0;
int result = -1;
int exactMatch = 0;
size_t bmabbrLen;
char *cp;
fp = OpenBookmarkFile(NULL);
if (fp == NULL)
return (-1);
bmabbrLen = strlen(bmabbr);
while (FGets(line, sizeof(line), fp) != NULL) {
if (ParseHostLine(line, bmp) < 0)
continue;
if (ISTREQ(bmp->bookmarkName, bmabbr)) {
/* Exact match, done. */
byBmNameFlag = bmabbrLen;
exactMatch = 1;
break;
} else if (ISTRNEQ(bmp->bookmarkName, bmabbr, bmabbrLen)) {
/* Remember this one, it matched an abbreviated
* bookmark name.
*/
byBmAbbr = *bmp;
byBmAbbrFlag = bmabbrLen;
} else if (ISTREQ(bmp->name, bmabbr)) {
/* Remember this one, it matched a full
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -