Jak podać nazwę użytkownika i hasło podczas łączenia się z udziałem sieciowym
Podczas łączenia się z udziałem sieciowym, dla którego bieżący użytkownik (w moim przypadku użytkownik usługi obsługującej sieć) nie ma żadnych praw, należy podać nazwę i hasło.
Wiem, jak to zrobić z funkcjami Win32( rodzina WNet*
z mpr.dll
), ale chciałbym to zrobić z funkcjonalnością.Net (2.0).
Jakie opcje są dostępne?
Może trochę więcej informacji pomoże:
- przypadek użycia to usługa windows, a nie Asp.Net podanie.
- usługa działa na koncie, które nie ma praw do udziału.
- konto użytkownika potrzebne do udziału nie jest znane po stronie klienta.
- klient i serwer nie są członkami tej samej domeny.
11 answers
Możesz zmienić tożsamość wątku lub P / wywołać WNetAddConnection2. Wolę to drugie, ponieważ czasami muszę utrzymywać wiele poświadczeń dla różnych lokalizacji. I zawijam go do IDisposable i wywołuję WNetCancelConnection2, aby później usunąć creds (unikając błędu wielu nazw użytkowników):
using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
File.Copy(@"\\server\read\file", @"\\server2\write\file");
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-17 14:39:18
Tak bardzo spodobała mi się odpowiedź Marka Bracketta , że sama szybko ją wdrożyłam. Tutaj jest, jeśli ktoś jeszcze potrzebuje go w pośpiechu:
public class NetworkConnection : IDisposable
{
string _networkName;
public NetworkConnection(string networkName,
NetworkCredential credentials)
{
_networkName = networkName;
var netResource = new NetResource()
{
Scope = ResourceScope.GlobalNetwork,
ResourceType = ResourceType.Disk,
DisplayType = ResourceDisplaytype.Share,
RemoteName = networkName
};
var userName = string.IsNullOrEmpty(credentials.Domain)
? credentials.UserName
: string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);
var result = WNetAddConnection2(
netResource,
credentials.Password,
userName,
0);
if (result != 0)
{
throw new Win32Exception(result);
}
}
~NetworkConnection()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
WNetCancelConnection2(_networkName, 0, true);
}
[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NetResource netResource,
string password, string username, int flags);
[DllImport("mpr.dll")]
private static extern int WNetCancelConnection2(string name, int flags,
bool force);
}
[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
public ResourceScope Scope;
public ResourceType ResourceType;
public ResourceDisplaytype DisplayType;
public int Usage;
public string LocalName;
public string RemoteName;
public string Comment;
public string Provider;
}
public enum ResourceScope : int
{
Connected = 1,
GlobalNetwork,
Remembered,
Recent,
Context
};
public enum ResourceType : int
{
Any = 0,
Disk = 1,
Print = 2,
Reserved = 8,
}
public enum ResourceDisplaytype : int
{
Generic = 0x0,
Domain = 0x01,
Server = 0x02,
Share = 0x03,
File = 0x04,
Group = 0x05,
Network = 0x06,
Root = 0x07,
Shareadmin = 0x08,
Directory = 0x09,
Tree = 0x0a,
Ndscontainer = 0x0b
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-26 12:09:02
Dzisiaj 7 lat później mam ten sam problem i chciałbym podzielić się moją wersją rozwiązania.
Jest gotowy do kopiowania i wklejania: -) oto jest:
Krok 1
W Twoim kodzie (gdy potrzebujesz zrobić coś z uprawnieniami)
ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate
{
//Your code here
//Let's say file copy:
if (!File.Exists(to))
{
File.Copy(from, to);
}
});
Krok 2
Plik pomocniczy, który robi magię
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
namespace BlaBla
{
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
public class ImpersonationHelper
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Impersonate(string domainName, string userName, string userPassword, Action actionToExecute)
{
SafeTokenHandle safeTokenHandle;
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, domainName, userPassword,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
//Facade.Instance.Trace("LogonUser called.");
if (returnValue == false)
{
int ret = Marshal.GetLastWin32Error();
//Facade.Instance.Trace($"LogonUser failed with error code : {ret}");
throw new System.ComponentModel.Win32Exception(ret);
}
using (safeTokenHandle)
{
//Facade.Instance.Trace($"Value of Windows NT token: {safeTokenHandle}");
//Facade.Instance.Trace($"Before impersonation: {WindowsIdentity.GetCurrent().Name}");
// Use the token handle returned by LogonUser.
using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
{
using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
{
//Facade.Instance.Trace($"After impersonation: {WindowsIdentity.GetCurrent().Name}");
//Facade.Instance.Trace("Start executing an action");
actionToExecute();
//Facade.Instance.Trace("Finished executing an action");
}
}
//Facade.Instance.Trace($"After closing the context: {WindowsIdentity.GetCurrent().Name}");
}
}
catch (Exception ex)
{
//Facade.Instance.Trace("Oh no! Impersonate method failed.");
//ex.HandleException();
//On purpose: we want to notify a caller about the issue /Pavel Kovalev 9/16/2016 2:15:23 PM)/
throw;
}
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 21:39:44
Przeszukałem wiele metod i zrobiłem to po swojemu. Musisz otworzyć połączenie między dwoma maszynami za pomocą polecenia wiersz polecenia NET USE i po zakończeniu pracy Wyczyść połączenie za pomocą polecenia net USE "myconnection" /delete.
Musisz użyć procesu wiersza polecenia z kodu z tyłu, tak:
var savePath = @"\\servername\foldername\myfilename.jpg";
var filePath = @"C:\\temp\myfileTosave.jpg";
Użycie jest proste:
SaveACopyfileToServer(filePath, savePath);
Oto funkcje:
using System.IO
using System.Diagnostics;
public static void SaveACopyfileToServer(string filePath, string savePath)
{
var directory = Path.GetDirectoryName(savePath).Trim();
var username = "loginusername";
var password = "loginpassword";
var filenameToSave = Path.GetFileName(savePath);
if (!directory.EndsWith("\\"))
filenameToSave = "\\" + filenameToSave;
var command = "NET USE " + directory + " /delete";
ExecuteCommand(command, 5000);
command = "NET USE " + directory + " /user:" + username + " " + password;
ExecuteCommand(command, 5000);
command = " copy \"" + filePath + "\" \"" + directory + filenameToSave + "\"";
ExecuteCommand(command, 5000);
command = "NET USE " + directory + " /delete";
ExecuteCommand(command, 5000);
}
A także funkcja ExecuteCommand to:
public static int ExecuteCommand(string command, int timeout)
{
var processInfo = new ProcessStartInfo("cmd.exe", "/C " + command)
{
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = "C:\\",
};
var process = Process.Start(processInfo);
process.WaitForExit(timeout);
var exitCode = process.ExitCode;
process.Close();
return exitCode;
}
Ta funkcja działała bardzo dla mnie szybki i stabilny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-17 13:09:32
Rozwiązanie Luke Chinane wygląda dobrze, ale zadziałało tylko częściowo w moim ASP.NET aplikacja MVC. Mając dwa udziały na tym samym serwerze z różnymi poświadczeniami mogłem użyć podszywania się tylko dla pierwszego.
Problem z WNetAddConnection2 polega również na tym, że zachowuje się on inaczej w różnych wersjach systemu windows. Dlatego Szukałem alternatyw i znalazłem funkcję LogonUser . Oto Mój kod który działa również w ASP.NET:
public sealed class WrappedImpersonationContext
{
public enum LogonType : int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkClearText = 8,
NewCredentials = 9
}
public enum LogonProvider : int
{
Default = 0, // LOGON32_PROVIDER_DEFAULT
WinNT35 = 1,
WinNT40 = 2, // Use the NTLM logon provider.
WinNT50 = 3 // Use the negotiate logon provider.
}
[DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain,
String lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll")]
public extern static bool CloseHandle(IntPtr handle);
private string _domain, _password, _username;
private IntPtr _token;
private WindowsImpersonationContext _context;
private bool IsInContext
{
get { return _context != null; }
}
public WrappedImpersonationContext(string domain, string username, string password)
{
_domain = String.IsNullOrEmpty(domain) ? "." : domain;
_username = username;
_password = password;
}
// Changes the Windows identity of this thread. Make sure to always call Leave() at the end.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Enter()
{
if (IsInContext)
return;
_token = IntPtr.Zero;
bool logonSuccessfull = LogonUser(_username, _domain, _password, LogonType.NewCredentials, LogonProvider.WinNT50, ref _token);
if (!logonSuccessfull)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
WindowsIdentity identity = new WindowsIdentity(_token);
_context = identity.Impersonate();
Debug.WriteLine(WindowsIdentity.GetCurrent().Name);
}
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Leave()
{
if (!IsInContext)
return;
_context.Undo();
if (_token != IntPtr.Zero)
{
CloseHandle(_token);
}
_context = null;
}
}
Użycie:
var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password);
impersonationContext.Enter();
//do your stuff here
impersonationContext.Leave();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-18 14:49:13
Jedną z opcji, która może działać, jest użycie WindowsIdentity.Impersonate
(i zmiana głównego wątku), aby stać się pożądanym użytkownikiem, tak jak . Obawiam się jednak, że powrót do P/invoke...
Kolejną bezczelną (i równie daleką od ideału) opcją może być wywołanie procesu do wykonania pracy... ProcessStartInfo
.UserName
, .Password
i .Domain
.
wreszcie-może uruchomić usługę na dedykowanym koncie, które ma dostęp? (usunięte, ponieważ wyjaśniłeś, że nie jest to opcja).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-17 13:23:11
OK... Mogę odtworzyć..
Zastrzeżenie: właśnie miałem 18 + godzinny dzień (ponownie).. Jestem stary i zapomniany.. Nie umiem przeliterować.. Mam krótki zasięg uwagi, więc lepiej reaguję szybko.. :-)
Pytanie:
Czy możliwa jest zmiana głównego wątku na użytkownika bez konta na komputerze lokalnym?
Odpowiedź:
Tak, możesz zmienić główny wątek, nawet jeśli używane poświadczenia nie są zdefiniowane lokalnie lub znajdują się poza "lasem".
I just napotkał ten problem podczas próby połączenia się z serwerem SQL za pomocą uwierzytelniania NTLM z usługi. To połączenie wykorzystuje poświadczenia powiązane z procesem, co oznacza, że musisz uwierzytelnić konto lokalne lub konto domeny, zanim będziesz mógł się podszywać. Bla, bla...
Ale...
Wywołanie LogonUser(..) z atrybutem ????_NEW_CREDENTIALS zwróci token bezpieczeństwa bez próby uwierzytelnienia poświadczeń. Kewl.. Nie musisz definiować konta w "lesie". Po uzyskaniu tokenu może być konieczne wywołanie metody DuplicateToken () z opcją włączenia podszywania się, co spowoduje utworzenie nowego tokenu. Teraz wywołaj SetThreadToken (NULL, token); (może to być &token?).. Wywołanie Perseonateloggedonuser( token); może być wymagane, ale nie sądzę. Sprawdź to..
Rób, co musisz..
Call RevertToSelf () if you called ImpersonateLoggedonUser () then SetThreadToken (NULL, NULL); (I think... sprawdzić), a następnie CloseHandle() na utworzonych uchwytach..
Żadnych obietnic, ale to zadziałało na mnie... To jest poza czubkiem mojej głowy (jak moje włosy) i nie mogę przeliterować!!!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-30 08:12:10
Dla VB.lovers the VB.NET odpowiednik kodu Luke 'a Quinane' a (dzięki Luke!)
Imports System
Imports System.Net
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Public Class NetworkConnection
Implements IDisposable
Private _networkName As String
Public Sub New(networkName As String, credentials As NetworkCredential)
_networkName = networkName
Dim netResource = New NetResource() With {
.Scope = ResourceScope.GlobalNetwork,
.ResourceType = ResourceType.Disk,
.DisplayType = ResourceDisplaytype.Share,
.RemoteName = networkName
}
Dim userName = If(String.IsNullOrEmpty(credentials.Domain), credentials.UserName, String.Format("{0}\{1}", credentials.Domain, credentials.UserName))
Dim result = WNetAddConnection2(NetResource, credentials.Password, userName, 0)
If result <> 0 Then
Throw New Win32Exception(result, "Error connecting to remote share")
End If
End Sub
Protected Overrides Sub Finalize()
Try
Dispose (False)
Finally
MyBase.Finalize()
End Try
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose (True)
GC.SuppressFinalize (Me)
End Sub
Protected Overridable Sub Dispose(disposing As Boolean)
WNetCancelConnection2(_networkName, 0, True)
End Sub
<DllImport("mpr.dll")> _
Private Shared Function WNetAddConnection2(netResource As NetResource, password As String, username As String, flags As Integer) As Integer
End Function
<DllImport("mpr.dll")> _
Private Shared Function WNetCancelConnection2(name As String, flags As Integer, force As Boolean) As Integer
End Function
End Class
<StructLayout(LayoutKind.Sequential)> _
Public Class NetResource
Public Scope As ResourceScope
Public ResourceType As ResourceType
Public DisplayType As ResourceDisplaytype
Public Usage As Integer
Public LocalName As String
Public RemoteName As String
Public Comment As String
Public Provider As String
End Class
Public Enum ResourceScope As Integer
Connected = 1
GlobalNetwork
Remembered
Recent
Context
End Enum
Public Enum ResourceType As Integer
Any = 0
Disk = 1
Print = 2
Reserved = 8
End Enum
Public Enum ResourceDisplaytype As Integer
Generic = &H0
Domain = &H1
Server = &H2
Share = &H3
File = &H4
Group = &H5
Network = &H6
Root = &H7
Shareadmin = &H8
Directory = &H9
Tree = &HA
Ndscontainer = &HB
End Enum
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-05 15:42:15
Powinieneś patrzeć na dodanie takiego typu:
<identity impersonate="true" userName="domain\user" password="****" />
Do twojej sieci.config.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-17 13:12:23
Jeśli nie możesz utworzyć lokalnego tokenu bezpieczeństwa, wygląda na to, że wykluczyłeś wszystkie wszystkie paski opcji Win32 API i WNetAddConnection*.
Mnóstwo informacji na MSDN o informacji WNet-PInvoke i przykładowym kodzie, który łączy się ze ścieżką UNC tutaj:
Http://www.pinvoke.net/default.aspx/mpr/WNetAddConnection2.html#
Odniesienie do MSDN tutaj:
Http://msdn.microsoft.com/en-us/library/aa385391 (VS.85). aspx
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-17 14:35:27
Również przeniesiony do F # {[3] } do użycia z FAKE
module NetworkShare
open System
open System.ComponentModel
open System.IO
open System.Net
open System.Runtime.InteropServices
type ResourceScope =
| Connected = 1
| GlobalNetwork = 2
| Remembered = 3
| Recent = 4
type ResourceType =
| Any = 0
| Disk = 1
| Print = 2
| Reserved = 8
type ResourceDisplayType =
| Generic = 0x0
| Domain = 0x01
| Server = 0x02
| Share = 0x03
| File = 0x04
| Group = 0x05
| Network = 0x06
| Root = 0x07
| Shareadmin = 0x08
| Directory = 0x09
| Tree = 0x0a
| Ndscontainer = 0x0b
//Uses of this construct may result in the generation of unverifiable .NET IL code.
#nowarn "9"
[<StructLayout(LayoutKind.Sequential)>]
type NetResource =
struct
val mutable Scope : ResourceScope
val mutable ResourceType : ResourceType
val mutable DisplayType : ResourceDisplayType
val mutable Usage : int
val mutable LocalName : string
val mutable RemoteName : string
val mutable Comment : string
val mutable Provider : string
new(name) = {
// lets preset needed fields
NetResource.Scope = ResourceScope.GlobalNetwork
ResourceType = ResourceType.Disk
DisplayType = ResourceDisplayType.Share
Usage = 0
LocalName = null
RemoteName = name
Comment = null
Provider = null
}
end
type WNetConnection(networkName : string, credential : NetworkCredential) =
[<Literal>]
static let Mpr = "mpr.dll"
[<DllImport(Mpr, EntryPoint = "WNetAddConnection2")>]
static extern int connect(NetResource netResource, string password, string username, int flags)
[<DllImport(Mpr, EntryPoint = "WNetCancelConnection2")>]
static extern int disconnect(string name, int flags, bool force)
let mutable disposed = false;
do
let userName = if String.IsNullOrWhiteSpace credential.Domain
then credential.UserName
else credential.Domain + "\\" + credential.UserName
let resource = new NetResource(networkName)
let result = connect(resource, credential.Password, userName, 0)
if result <> 0 then
let msg = "Error connecting to remote share " + networkName
new Win32Exception(result, msg)
|> raise
let cleanup(disposing:bool) =
if not disposed then
disposed <- true
if disposing then () // TODO dispose managed resources here
disconnect(networkName, 0, true) |> ignore
interface IDisposable with
member __.Dispose() =
disconnect(networkName, 0, true) |> ignore
GC.SuppressFinalize(__)
override __.Finalize() = cleanup(false)
type CopyPath =
| RemotePath of string * NetworkCredential
| LocalPath of string
let createDisposable() =
{
new IDisposable with
member __.Dispose() = ()
}
let copyFile overwrite destPath srcPath : unit =
use _srcConn =
match srcPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
use _destConn =
match destPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
match srcPath, destPath with
| RemotePath(src, _), RemotePath(dest, _)
| LocalPath(src), RemotePath(dest, _)
| RemotePath(src, _), LocalPath(dest)
| LocalPath(src), LocalPath(dest) ->
if FileInfo(src).Exists |> not then
failwith ("Source file not found: " + src)
let destFilePath =
if DirectoryInfo(dest).Exists then Path.Combine(dest, Path.GetFileName src)
else dest
File.Copy(src, destFilePath, overwrite)
let rec copyDir copySubDirs filePattern destPath srcPath =
use _srcConn =
match srcPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
use _destConn =
match destPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
match srcPath, destPath with
| RemotePath(src, _), RemotePath(dest, _)
| LocalPath(src), RemotePath(dest, _)
| RemotePath(src, _), LocalPath(dest)
| LocalPath(src), LocalPath(dest) ->
let dir = DirectoryInfo(src)
if dir.Exists |> not then
failwith ("Source directory not found: " + src)
let dirs = dir.GetDirectories()
if Directory.Exists(dest) |> not then
Directory.CreateDirectory(dest) |> ignore
let files = dir.GetFiles(filePattern)
for file in files do
let tempPath = Path.Combine(dest, file.Name)
file.CopyTo(tempPath, false) |> ignore
if copySubDirs then
for subdir in dirs do
let subdirSrc =
match srcPath with
| RemotePath(_, credential) -> RemotePath(Path.Combine(dest, subdir.Name), credential)
| LocalPath(_) -> LocalPath(Path.Combine(dest, subdir.Name))
let subdirDest =
match destPath with
| RemotePath(_, credential) -> RemotePath(subdir.FullName, credential)
| LocalPath(_) -> LocalPath(subdir.FullName)
copyDir copySubDirs filePattern subdirDest subdirSrc
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-10 16:22:04