-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgram.cs
308 lines (275 loc) · 12.1 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Win32;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace ExportOutlookPSTs
{
class Program
{
static void Main(string[] args)
{
// Check if sharePath is provided as an argument
if (args.Length == 0)
{
// Log error message to a default location
string defaultErrorLog = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ExportOutlookPSTs_ErrorLog.txt");
string errorMessage = $"[{DateTime.Now}] - {Environment.UserName} - Error: No sharePath provided as a command-line argument.";
LogError(defaultErrorLog, errorMessage);
return;
}
string sharePath = args[0];
// Validate sharePath format
if (!sharePath.StartsWith(@"\\"))
{
string defaultErrorLog = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ExportOutlookPSTs_ErrorLog.txt");
string errorMessage = $"[{DateTime.Now}] - {Environment.UserName} - Error: Invalid network share path format: '{sharePath}'. Ensure it starts with '\\\\'.";
LogError(defaultErrorLog, errorMessage);
return;
}
string username = Environment.UserName;
string logFile = Path.Combine(sharePath, $"{username}.txt");
string errorLog = Path.Combine(sharePath, "ErrorLog.txt");
string noPstLog = Path.Combine(sharePath, "_NoPST.txt");
try
{
// Check if the network share is accessible
if (!Directory.Exists(sharePath))
{
throw new DirectoryNotFoundException($"The network share path '{sharePath}' is not accessible.");
}
string logEntry;
// Check if an Outlook profile exists
if (!OutlookProfileExists())
{
// No Outlook profiles found
logEntry = $"{username} - No Outlook profile - checked: {DateTime.Now}";
UpdateNoPstLog(noPstLog, username, logEntry);
return;
}
// Initialize Outlook COM object
Outlook.Application outlookApp = null;
Outlook.NameSpace mapiNamespace = null;
try
{
outlookApp = new Outlook.Application();
// Get MAPI namespace without displaying UI
mapiNamespace = outlookApp.GetNamespace("MAPI");
mapiNamespace.Logon("", "", Missing.Value, false);
}
catch (COMException comEx)
{
throw new Exception($"COM Exception: Failed to create Outlook COM object or get MAPI namespace: {comEx.Message}", comEx);
}
catch (Exception ex)
{
throw new Exception($"General Exception: Failed to create Outlook COM object or get MAPI namespace: {ex.Message}", ex);
}
// Collect PST paths
var pstPaths = mapiNamespace.Stores
.Cast<Outlook.Store>()
.Select(store => store.FilePath)
.Where(path => !string.IsNullOrEmpty(path) && File.Exists(path) && path.EndsWith(".pst", StringComparison.OrdinalIgnoreCase))
.Select(NormalizePath)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
// Log off MAPI namespace
mapiNamespace.Logoff();
// Release COM objects
Marshal.ReleaseComObject(mapiNamespace);
mapiNamespace = null;
Marshal.ReleaseComObject(outlookApp);
outlookApp = null;
if (pstPaths.Count == 0)
{
// No PST files found
logEntry = $"{username} - No PST files - checked: {DateTime.Now}";
UpdateNoPstLog(noPstLog, username, logEntry);
return;
}
// Read existing log entries and normalize them
var existingEntries = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (File.Exists(logFile))
{
foreach (var line in File.ReadAllLines(logFile))
{
var normalized = NormalizePath(line.Trim());
if (!string.IsNullOrEmpty(normalized))
existingEntries.Add(normalized);
}
}
// Determine new entries not yet in the log
var newEntries = pstPaths.Where(p => !existingEntries.Contains(p)).ToList();
if (newEntries.Count > 0)
{
// Add new entries to the log
File.AppendAllLines(logFile, newEntries);
}
// If there are no new entries, do nothing
// Remove user from _NoPST.txt if they now have PST files
RemoveUserFromNoPstLog(noPstLog, username);
}
catch (Exception ex)
{
// Log unexpected errors
string errorMessage = $"[{DateTime.Now}] - {username} - Unexpected error in main script: {ex.Message}";
LogError(errorLog, errorMessage);
}
}
/// <summary>
/// Checks if an Outlook profile exists by inspecting the registry.
/// </summary>
/// <returns>True if a profile exists, otherwise false.</returns>
private static bool OutlookProfileExists()
{
// Possible Outlook versions
string[] outlookVersions = { "16.0", "15.0", "14.0", "12.0", "11.0" };
foreach (var version in outlookVersions)
{
string profileKeyPath = $@"Software\Microsoft\Office\{version}\Outlook\Profiles";
using (RegistryKey profileKey = Registry.CurrentUser.OpenSubKey(profileKeyPath))
{
if (profileKey != null)
{
string[] profiles = profileKey.GetSubKeyNames();
if (profiles != null && profiles.Length > 0)
{
// Profiles exist
return true;
}
}
}
}
// No profiles found
return false;
}
/// <summary>
/// Normalizes the file path by removing the '\\?\UNC\' prefix if present.
/// </summary>
/// <param name="path">The original file path.</param>
/// <returns>The normalized file path.</returns>
private static string NormalizePath(string path)
{
if (path.StartsWith(@"\\?\UNC\", StringComparison.OrdinalIgnoreCase))
{
return @"\\" + path.Substring(8);
}
return path;
}
/// <summary>
/// Logs users without PST files or Outlook profiles to the _NoPST.txt file without duplicates.
/// Updates the existing entry if the user is already in the log.
/// </summary>
/// <param name="noPstLogPath">Path to the NoPST log file.</param>
/// <param name="username">The username to log.</param>
/// <param name="logEntry">The log entry to add or update.</param>
private static void UpdateNoPstLog(string noPstLogPath, string username, string logEntry)
{
try
{
// Ensure the directory exists
string directory = Path.GetDirectoryName(noPstLogPath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var logLines = new List<string>();
// Read existing log entries
if (File.Exists(noPstLogPath))
{
logLines = File.ReadAllLines(noPstLogPath).ToList();
// Check if user already exists in the log
bool userFound = false;
for (int i = 0; i < logLines.Count; i++)
{
if (logLines[i].StartsWith(username + " -", StringComparison.OrdinalIgnoreCase))
{
// Update the existing entry
logLines[i] = logEntry;
userFound = true;
break;
}
}
if (!userFound)
{
// Add new entry
logLines.Add(logEntry);
}
}
else
{
// Log file does not exist, create new entry
logLines.Add(logEntry);
}
// Write updated log entries back to the file
File.WriteAllLines(noPstLogPath, logLines);
}
catch
{
// If logging fails, do nothing since the application runs hidden
}
}
/// <summary>
/// Removes the user from the _NoPST.txt log file if they now have PST files.
/// </summary>
/// <param name="noPstLogPath">Path to the NoPST log file.</param>
/// <param name="username">The username to remove.</param>
private static void RemoveUserFromNoPstLog(string noPstLogPath, string username)
{
try
{
if (!File.Exists(noPstLogPath))
{
return;
}
var logLines = File.ReadAllLines(noPstLogPath).ToList();
bool userFound = false;
// Remove the user's entry if it exists
for (int i = 0; i < logLines.Count; i++)
{
if (logLines[i].StartsWith(username + " -", StringComparison.OrdinalIgnoreCase))
{
logLines.RemoveAt(i);
userFound = true;
break;
}
}
if (userFound)
{
// Write updated log entries back to the file
File.WriteAllLines(noPstLogPath, logLines);
}
}
catch
{
// If operation fails, do nothing since the application runs hidden
}
}
/// <summary>
/// Logs error messages to the specified error log file.
/// </summary>
/// <param name="errorLogPath">Path to the error log file.</param>
/// <param name="message">Error message to log.</param>
private static void LogError(string errorLogPath, string message)
{
try
{
// Ensure the directory exists
string directory = Path.GetDirectoryName(errorLogPath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// Append the error message to the error log file
File.AppendAllText(errorLogPath, message + Environment.NewLine);
}
catch
{
// If logging fails, do nothing since the application runs hidden
}
}
}
}