473,324 Members | 2,166 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,324 software developers and data experts.

"FTP Pathname Glob BO" detected when using FtpWebRequest

I am using the following code to connect to and download files from an ftp
server, but the Symantec anti-virus software on some of my customers machines
tells them that their computers are attacking the Ftp server with "FTP
Pathname Glob BO". See the following link for more info:

http://www.symantec.com/avcenter/att...gs/s20430.html

This is standard .Net code, so I'm not understanding why it would trigger
such a response. Also, other searching on that name brings up almost nothing.

The only thing that I can guess is that it's because I'm using /%2E%2E/ to
move up a couple of directories. But the Uri class removes /../, so I had to
use the former. And I do have to move up two directories from where the ftp
login initially places me.

private void Form1_Load(object sender, EventArgs e)
{
Uri ftpConnectionString = new Uri("ftp://192.168.1.1/%2E%2E/%2E%2E/SIF1/IN");
FtpWebRequest ftpConnection =
(FtpWebRequest)FtpWebRequest.Create(ftpConnectionS tring);

ftpConnection.Credentials = new NetworkCredential("username", "password");
ftpConnection.KeepAlive = false;
ftpConnection.UseBinary = true;
ftpConnection.UsePassive = false;
ftpConnection.Method = WebRequestMethods.Ftp.GetFileSize;

using (FtpWebResponse ftpResponse =
(FtpWebResponse)ftpConnection.GetResponse())
{
long fileSize = ftpResponse.ContentLength;
ftpResponse.Close();
MessageBox.Show(fileSize.ToString());
}
}
}
Jul 30 '07 #1
10 3158
Hi,

Could you please tell us why you're using "ftp://192.168.1.1/../../"? Based
on my understanding, the Uri class will support "/../" as long as the path
is legal:

Uri u = new Uri("ftp://192.168.0.1/test/../temp/foo.txt");
Console.WriteLine(u.ToString());

The code will print "ftp://192.168.0.1/temp/foo.txt".

Do you mean that the ".." part is an existing path on the ftp server?

Also, I don't think this is an issue of the .NET network library. If you
use other ftp clients, the anti-virus software will probably also treat it
as an attack since you're trying to access path beyond the intended scope.
Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Jul 31 '07 #2
Your example would work. The difference is that the /../ is at the beginning
of the path in mine. Uri treats

Uri ftpConnectionString = new Uri("ftp://192.168.1.1/../../SIF1/IN");

the same as

Uri ftpConnectionString = new Uri("ftp://192.168.1.1/SIF1/IN");

The directory structure is the following:

root
>users
>>username
SIF
When you log in, it puts you in the username directory, so you have to go up
two to the root and then down one to SIF. Other ftp clients all work because
they log in and then change directories. If you can tell me how to make the
..Net libraries log in, navigate directories, and then download files, I'd be
more than happy to do that. Right now I don't believe they're capable of that
because they inherit from WebRequest. You have to specify the whole path in
advance.

I also haven't been able to find anywhere in the Symantec software to
specifically unblock my program and their tech support if far less than
helpful with this sort of thing. This might belong in another forum, but does
anyone know how to unblock a program in Norton Internet Security?
Jul 31 '07 #3
Hi,

I believe the "other ftp clients" will first login to the ftp site, then
use additional FTP commands to change directory, and get the file; that's
why the "/../" following the ftp root works.

I'm curious to know if you turn off the anti-virous software, will it work?

Anyway, .NET Framework currently doesn't have a ftp client built-in, but
there's many open source ftp client in .NET, for example:

http://csharp-source.net/open-source/network-clients
Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Aug 1 '07 #4
Yeah, it works fine on every other computer. And I'm certain that every one
of my customers has other anti-virus software too. It only the symantec that
catches it.

Also, I'm selling this software, so I thought I couldn't use open-source
code because I'd have to distribute the code under the GNU license.

Aug 1 '07 #5
Hi,

I'm writing to check the status of this post. Since .NET Framework
WebClient/WebRequest is currently using Uri class to represent the URL and
it will remove the "/../" part from the URL, we cannot use them here.
That's why I suggested to use WiniNet api instead. If this is a simple ftp
downloading, I think this should be ok. Anyway, please feel free to let me
know if you have any concerns or anything unclear. Thanks.
Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Aug 6 '07 #6
Sorry for the delayed response, but yes it does work great.

The same thing seems to happen in Vista as well, so this workaround is
multi-purpose.

I have been having some trouble trying to get a status callback to work so I
can report how much of the file has been downloaded. Right now the following
code never calls the InternetStatusCallback function. Everything compiles and
runs without error, but it never fires the callback.

I've never used interop before, so I'm guessing it has something to do with
either the InternetSetStatusCallback definition or the
INTERNET_STATUS_CALLBACK delegate.

Thanks again
Matt
public class MasterFtp : IDisposable
{
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetOpen(
string lpszAgent,
int dwAccessType,
string lpszProxyName,
string lpszProxyBypass,
int dwFlags);

[DllImport("wininet.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool InternetCloseHandle(IntPtr hInternet);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetConnect(
IntPtr hInternet,
string lpszServerName,
short nServerPort,
string lpszUsername,
string lpszPassword,
int dwService,
int dwFlags,
IntPtr dwContext);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern bool FtpGetCurrentDirectory(
IntPtr hConnect,
StringBuilder directory,
ref int bufferLength);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern bool FtpSetCurrentDirectory(
IntPtr hFtpConnection,
string lpszDirectory);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr FtpOpenFile(
IntPtr hConnect,
string lpszFileName,
int dwAccess,
int dwFlags,
IntPtr dwContext);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern uint FtpGetFileSize(
IntPtr hFile,
out ulong lpdwFileSizeHigh);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FtpGetFile(
IntPtr hConnect,
string remoteFile,
string newFile,
[MarshalAs(UnmanagedType.Bool)] bool failIfExists,
int flagsAndAttributes,
int flags,
IntPtr context);

[DllImport("wininet.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool InternetGetLastResponseInfo(
out int errorCode,
StringBuilder buffer,
ref int bufferLength);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetSetStatusCallback(
IntPtr hInternet,
INTERNET_STATUS_CALLBACK lpfnInternetCallback);

private delegate void INTERNET_STATUS_CALLBACK(
IntPtr hInternet,
IntPtr dwContext,
InternetStatus dwInternetStatus,
InternetState lpvStatusInformation,
int dwStatusInformationLength);

private enum InternetStatus
{
ResolvingName = 10,
NameResolved = 11,
ConnectingToServer = 20,
ConnectedToServer = 21,
SendingRequest = 30,
RequestSent = 31,
ReceivingResponse = 40,
ResponseReceived = 41,
CtlResponseReceived = 42,
Prefetch = 43,
ClosingConnection = 50,
ConnectionClosed = 51,
HandleCreated = 60,
HandleClosing = 70,
RequestComplete = 100,
Redirect = 110,
IntermediateResponse = 120,
StateChange = 200
}

private enum InternetState
{
Connected = 1,
Disconnected = 2,
DisconnectedByUser = 16,
Idle = 256,
Busy = 512
}

private enum ConnectionState
{
Internet,
Connection,
Directory,
Size,
Download
}

const int ERROR_SUCCESS = 0;
const int INTERNET_OPEN_TYPE_PRECONFIG = 0;
const int INTERNET_SERVICE_FTP = 1;
const int INTERNET_FLAG_PASSIVE = 0x8000000;
const int FTP_TRANSFER_BINARY = 0x2;
const int FTP_TRANSFER_ASCII = 0x1;
const int GENERIC_READ = unchecked((int)0x80000000);
const int INTERNET_INVALID_STATUS_CALLBACK = -1;
int MAX_PATH = 260;

private IntPtr hOpen;
private IntPtr hConnection;

public MasterFtp(string ftpHost, short port, string user, string
password, bool usePasv)
{
hOpen = InternetOpen("C# FtpDownloader",
INTERNET_OPEN_TYPE_PRECONFIG, null, null, 0);

if (hOpen == IntPtr.Zero)
GetError(ConnectionState.Internet);

hConnection = InternetConnect(hOpen, ftpHost, port, user,
password, INTERNET_SERVICE_FTP, usePasv ? INTERNET_FLAG_PASSIVE : 0, new
IntPtr());

if (hConnection == IntPtr.Zero)
GetError(ConnectionState.Connection);
}

public void SetDirectory(string directory)
{
StringBuilder dir = new StringBuilder(MAX_PATH);

foreach (string currentChange in directory.Split('\\'))
{
MAX_PATH = 260;
dir = new StringBuilder(MAX_PATH);

if (!FtpGetCurrentDirectory(hConnection, dir, ref MAX_PATH))
GetError(ConnectionState.Directory);

Trace.WriteLine(dir, "Current Directory");
Trace.WriteLine(currentChange, "Change");

if (!FtpSetCurrentDirectory(hConnection, currentChange))
GetError(ConnectionState.Directory);

Trace.WriteLine("Changed");

MAX_PATH = 260;
dir = new StringBuilder(MAX_PATH);

if (!FtpGetCurrentDirectory(hConnection, dir, ref MAX_PATH))
GetError(ConnectionState.Directory);

Trace.WriteLine(dir, "New Directory");
}
}

public uint GetFileSize(string remoteFile)
{
IntPtr hFile = FtpOpenFile(hConnection, remoteFile,
GENERIC_READ, FTP_TRANSFER_BINARY, new IntPtr());

if (hFile == IntPtr.Zero)
GetError(ConnectionState.Size);

ulong sizeHigh = 0;

return FtpGetFileSize(hFile, out sizeHigh);
}

public void DownloadFile(string remoteFile, string localFile, bool
bFailIfExists)
{
INTERNET_STATUS_CALLBACK callback = new
INTERNET_STATUS_CALLBACK(InternetStatusCallback);

IntPtr callbackPtr = InternetSetStatusCallback(hOpen, callback);

if (!FtpGetFile(hConnection, remoteFile, localFile,
bFailIfExists, (int)FileAttributes.Normal, FTP_TRANSFER_BINARY, new IntPtr()))
GetError(ConnectionState.Download);
}

private void InternetStatusCallback(IntPtr hInternet,
IntPtr dwContext,
InternetStatus dwInternetStatus,
InternetState lpvStatusInformation,
int dwStatusInformationLength)
{
Trace.WriteLine(dwInternetStatus + " - " + lpvStatusInformation
+ " - " + dwStatusInformationLength);
}

private void GetError(ConnectionState state)
{
int errorCode = Marshal.GetLastWin32Error();

Trace.WriteLine("Ftp Error: " + errorCode);
}

#region IDisposable Members

public void Dispose()
{
if (hConnection != IntPtr.Zero)
{
InternetCloseHandle(hConnection);
hConnection = IntPtr.Zero;
}
if (hOpen != IntPtr.Zero)
{
InternetCloseHandle(hOpen);
hOpen = IntPtr.Zero;
}
}

#endregion
}
}

Aug 7 '07 #7
Hi Matt,

I currently don't have a sample code on the InternetSetStatusCallback at
hand. It will take me some time to bring up a working sample for you. I'll
keep you posted as soon as possible. Thank you for your patience and
understanding.

Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Aug 8 '07 #8
Hi Matt,

I've done some research and managed to make the callback to work in your
code:

public class MasterFtp : IDisposable
{
[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetOpen(
string lpszAgent,
int dwAccessType,
string lpszProxyName,
string lpszProxyBypass,
int dwFlags);

[DllImport("wininet.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool InternetCloseHandle(IntPtr hInternet);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetConnect(
IntPtr hInternet,
string lpszServerName,
short nServerPort,
string lpszUsername,
string lpszPassword,
int dwService,
int dwFlags,
IntPtr dwContext);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern bool FtpGetCurrentDirectory(
IntPtr hConnect,
StringBuilder directory,
ref int bufferLength);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern bool FtpSetCurrentDirectory(
IntPtr hFtpConnection,
string lpszDirectory);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr FtpOpenFile(
IntPtr hConnect,
string lpszFileName,
int dwAccess,
int dwFlags,
IntPtr dwContext);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern uint FtpGetFileSize(
IntPtr hFile,
out ulong lpdwFileSizeHigh);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FtpGetFile(
IntPtr hConnect,
string remoteFile,
string newFile,
[MarshalAs(UnmanagedType.Bool)] bool failIfExists,
int flagsAndAttributes,
int flags,
IntPtr context);

[DllImport("wininet.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool InternetGetLastResponseInfo(
out int errorCode,
StringBuilder buffer,
ref int bufferLength);

[DllImport("wininet.dll", SetLastError = true, CharSet =
CharSet.Auto)]
static extern IntPtr InternetSetStatusCallback(
IntPtr hInternet,
INTERNET_STATUS_CALLBACK lpfnInternetCallback);

private delegate void INTERNET_STATUS_CALLBACK(
IntPtr hInternet,
IntPtr dwContext,
InternetStatus dwInternetStatus,
IntPtr lpvStatusInformation,
int dwStatusInformationLength);

private enum InternetStatus
{
ResolvingName = 10,
NameResolved = 11,
ConnectingToServer = 20,
ConnectedToServer = 21,
SendingRequest = 30,
RequestSent = 31,
ReceivingResponse = 40,
ResponseReceived = 41,
CtlResponseReceived = 42,
Prefetch = 43,
ClosingConnection = 50,
ConnectionClosed = 51,
HandleCreated = 60,
HandleClosing = 70,
RequestComplete = 100,
Redirect = 110,
IntermediateResponse = 120,
StateChange = 200
}

private enum InternetState
{
Connected = 1,
Disconnected = 2,
DisconnectedByUser = 16,
Idle = 256,
Busy = 512
}

private enum ConnectionState
{
Internet,
Connection,
Directory,
Size,
Download
}

const int ERROR_SUCCESS = 0;
const int INTERNET_OPEN_TYPE_PRECONFIG = 0;
const int INTERNET_SERVICE_FTP = 1;
const int INTERNET_FLAG_PASSIVE = 0x8000000;
const int FTP_TRANSFER_BINARY = 0x2;
const int FTP_TRANSFER_ASCII = 0x1;
const int GENERIC_READ = unchecked((int)0x80000000);
const int INTERNET_INVALID_STATUS_CALLBACK = -1;
int MAX_PATH = 260;

private IntPtr hOpen;
private IntPtr hConnection;
private IntPtr hContext;

public MasterFtp(string ftpHost, short port, string user, string
password, bool usePasv)
{
hContext = Marshal.AllocHGlobal(1);

hOpen = InternetOpen("C# FtpDownloader",
INTERNET_OPEN_TYPE_PRECONFIG, null, null, 0);

if (hOpen == IntPtr.Zero)
GetError(ConnectionState.Internet);

hConnection = InternetConnect(hOpen, ftpHost, port, user,
password, INTERNET_SERVICE_FTP, usePasv ? INTERNET_FLAG_PASSIVE : 0,
hContext);

if (hConnection == IntPtr.Zero)
GetError(ConnectionState.Connection);
}

public void SetDirectory(string directory)
{
StringBuilder dir = new StringBuilder(MAX_PATH);

foreach (string currentChange in directory.Split('\\'))
{
MAX_PATH = 260;
dir = new StringBuilder(MAX_PATH);

if (!FtpGetCurrentDirectory(hConnection, dir, ref MAX_PATH))
GetError(ConnectionState.Directory);

Trace.WriteLine(dir, "Current Directory");
Trace.WriteLine(currentChange, "Change");

if (!FtpSetCurrentDirectory(hConnection, currentChange))
GetError(ConnectionState.Directory);

Trace.WriteLine("Changed");

MAX_PATH = 260;
dir = new StringBuilder(MAX_PATH);

if (!FtpGetCurrentDirectory(hConnection, dir, ref MAX_PATH))
GetError(ConnectionState.Directory);

Trace.WriteLine(dir, "New Directory");
}
}

public uint GetFileSize(string remoteFile)
{
IntPtr hFile = FtpOpenFile(hConnection, remoteFile,
GENERIC_READ, FTP_TRANSFER_BINARY, new IntPtr());

if (hFile == IntPtr.Zero)
GetError(ConnectionState.Size);

ulong sizeHigh = 0;

return FtpGetFileSize(hFile, out sizeHigh);
}

INTERNET_STATUS_CALLBACK callback;

public void DownloadFile(string remoteFile, string localFile, bool
bFailIfExists)
{
callback = new INTERNET_STATUS_CALLBACK(InternetStatusCallback);
IntPtr callbackPtr = InternetSetStatusCallback(hConnection,
callback);

if (!FtpGetFile(hConnection, remoteFile, localFile,
bFailIfExists, (int)FileAttributes.Normal, FTP_TRANSFER_BINARY, hContext))
GetError(ConnectionState.Download);
}

private void InternetStatusCallback(IntPtr hInternet,
IntPtr dwContext,
InternetStatus dwInternetStatus,
IntPtr lpvStatusInformation,
int dwStatusInformationLength)
{
Trace.WriteLine(dwInternetStatus + " - " + lpvStatusInformation
+ " - " + dwStatusInformationLength);
}

private void GetError(ConnectionState state)
{
int errorCode = Marshal.GetLastWin32Error();

Trace.WriteLine("Ftp Error: " + errorCode);
}

#region IDisposable Members

public void Dispose()
{
if (hConnection != IntPtr.Zero)
{
InternetCloseHandle(hConnection);
hConnection = IntPtr.Zero;
}
if (hOpen != IntPtr.Zero)
{
InternetCloseHandle(hOpen);
hOpen = IntPtr.Zero;
}
if (hContext != IntPtr.Zero)
{
Marshal.FreeHGlobal(hContext);
hContext = IntPtr.Zero;
}
}

#endregion
}
Some changes:

1) The dwContext passed to InternetConnect and FtpGetFile should be
consistent, therefore I created a member variable hContext to track it,
it's initalized with Marshal.AllocHGlobal with 1 byte.

2) The delegate instance needs to stay at member scope, it must be kept
alive during the lifetime of the class.

3) The handle passed to InternetSetStatusCallback should be the handle
returned from InternetConnect instead of InternetOpen.
Hope this helps.
Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Aug 9 '07 #9
Forgot another change:

4) The lpvStatusInformation of INTERNET_STATUS_CALLBACK should be a pointer
instead of an integer:

[in] Address of a buffer that contains information pertinent to this call
to the callback function.

Please feel free to let me know if you find any other issues when
implementing the workaround.
Regards,
Walter Wang (wa****@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.

Aug 9 '07 #10
Works great.

Thanks,
Matt
Aug 13 '07 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: ralawrence | last post by:
Hi there, Funny question this, but the server my website is on is currently undergoing changes/updates and for a short period of time the version of PHP has gone back to 4.2.2. This means...
9
by: Marco Castro | last post by:
I tried to google for information of what the output of the "LIST" command means but couldn't find any articles that tell me exactly what every character means. I can figure out most of it, its...
6
by: JPSutor | last post by:
Does anyone have an example of a simple FTP trasfer app? All I need to do is transfer one file up to a web site and then retrieve it again. Every example I run into is loaded with code that, to...
8
by: mark.bergman | last post by:
I am porting from Digital Unix to Linux (RHEL 4), and am seeing a difference in the return value of glob(). Given a non-existant directory "/tmp/a", and the following line of code: result =...
1
by: =?Utf-8?B?QUcgTW9yZW5v?= | last post by:
I had a virus. Ran Norton Antivirus. Now when I start my computer I get "FTP Port is used" error message and no internet access, however, computer is connected to wireless home network. Any...
4
Manikgisl
by: Manikgisl | last post by:
hi, we have developed one application.That will download a zip file from ftp to enduser machine,,, For download a file from ftp ..we are using "Wininet API" to download But its not working...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.