· 4 years ago · Jul 04, 2021, 08:48 PM
1////////////////////////////////////////////////////////////////////////
2// File: services.cpp
3//
4//
5//
6////////////////////////////////////////////////////////////////////////
7
8#include "stdafx.h"
9
10#include <afxpriv.h> // for T2W()
11
12#include <io.h>
13#include <ctype.h>
14
15#include "qcerror.h"
16#include "services.h"
17
18#include "tlhelp32.h"
19
20#include "rs.h"
21#include "resource.h"
22
23#include "DebugNewHelpers.h"
24
25// The following strings were originally stored in Eudora as resources. This makes little sense since they are used
26// for interpreting date-time values that have day and month names that are always expressed in English. So, for
27// Hermes, I have made them embedded strings. This is simpler, more efficient and, I believe, more portable. Pete
28// Maclean, 8-Nov-2018.
29
30// Days of the week. Was resource IDS_WEEKDAYS.
31static const char szWeekdays_M[] = "SunMonTueWedThuFriSat";
32
33// Months of the year. Was resource IDS_MONTHS.
34static const char szMonths_M[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
35
36// Was resource IDS_SCAN_DATE_1.
37static const char szScanDate1_M[] = "%3s %d %d:%d:%d %d";
38
39// Was resource IDS_SCAN_DATE_2.
40static const char szScanDate2_M[] = "%d %3s %d %d:%d:%d";
41
42// Was resource IDS_SCAN_DATE_3.
43static const char szScanDate3_M[] = "%d %3s %d %d:%d:%d";
44
45static const char chrBackSlash = '\\';
46
47// I had to make the following change to get this to link in debug mode (Pete Maclean 4-Sep-2018)
48#undef DEBUG_NEW_NOTHROW
49#define DEBUG_NEW_NOTHROW new(std::nothrow)
50
51// API definitions
52#ifndef REPLACEFILE_WRITE_THROUGH
53 #define REPLACEFILE_WRITE_THROUGH 0x00000001
54#endif
55#ifndef REPLACEFILE_IGNORE_MERGE_ERRORS
56 #define REPLACEFILE_IGNORE_MERGE_ERRORS 0x00000002
57#endif
58
59#ifndef ERROR_UNABLE_TO_MOVE_REPLACEMENT
60 #define ERROR_UNABLE_TO_MOVE_REPLACEMENT 1176L
61#endif
62#ifndef ERROR_UNABLE_TO_MOVE_REPLACEMENT_2
63 #define ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 1177L
64#endif
65
66typedef BOOL (WINAPI * IndirectReplaceFile)(
67 LPCWSTR lpReplacedFileName, // file name
68 LPCWSTR lpReplacementFileName, // replacement file
69 LPCWSTR lpBackupFileName, // optional backup file
70 DWORD dwReplaceFlags, // replace options
71 LPVOID lpExclude, // reserved
72 LPVOID lpReserved ); // reserved
73
74// Constants
75DWORD kMinExtraFreeSpace = 1024 * 1024;
76 // Prefer to not have less than 1 Meg free
77
78
79// Local, non-exported function declarations
80void
81GetFileNameAndParentPath(
82 const char * in_szFullPath,
83 char * out_szParentPathBuffer,
84 int in_nParentPathBufferSize,
85 CString * out_szFileName = NULL);
86HRESULT
87MoveFileMaintainingCorrectPermissions(
88 const char * in_szOldName,
89 const char * in_szNewName,
90 DWORD in_dwFileSize,
91 DWORD in_dwFreeSpaceDestinationVolume);
92void
93SetLastErrorResult(
94 HRESULT* phr);
95
96
97////////////////////////////////////////////////////////////////////////
98// HashMT [extern, exported]
99//
100// The following hashing algorithm, KRHash, is derived from Karp & Rabin,
101// Harvard Center for Research in Computing Technology Tech. Report TR-31-81
102// The constant prime number in use is kKRHashPrime. It happens to be the largest
103// prime number that will fit in 31 bits, except for 2^31-1 itself....
104////////////////////////////////////////////////////////////////////////
105ULONG HashMT(const char* pszStr)
106{
107 const ULONG kKRHashPrime = 2147483629;
108
109 ULONG ulSum = 0; // returned
110
111 for (; *pszStr; pszStr++)
112 {
113 for (int nBit = 0x80; nBit != 0; nBit >>= 1)
114 {
115 ulSum += ulSum;
116 if (ulSum >= kKRHashPrime)
117 ulSum -= kKRHashPrime;
118
119 if ((*pszStr) & nBit)
120 ++ulSum;
121
122 if (ulSum>= kKRHashPrime)
123 ulSum -= kKRHashPrime;
124 }
125 }
126
127 return (ulSum + 1);
128}
129
130
131////////////////////////////////////////////////////////////////////////
132// HashMT [extern, exported]
133//
134// The following hashing algorithm, KRHash, is derived from Karp & Rabin,
135// Harvard Center for Research in Computing Technology Tech. Report TR-31-81
136// The constant prime number in use is kKRHashPrime. It happens to be the largest
137// prime number that will fit in 31 bits, except for 2^31-1 itself....
138////////////////////////////////////////////////////////////////////////
139ULONG HashMTIgnoreCase(const char* pszStr)
140{
141 const ULONG kKRHashPrime = 2147483629;
142
143 ULONG ulSum = 0; // returned
144
145 for (; *pszStr; pszStr++)
146 {
147 for (int nBit = 0x80; nBit != 0; nBit >>= 1)
148 {
149 ulSum += ulSum;
150 if (ulSum >= kKRHashPrime)
151 ulSum -= kKRHashPrime;
152
153 // here we create the hash always by ignoring the hash values
154 char ch = *pszStr;
155 if (tolower(ch) & nBit)
156 ++ulSum;
157
158 if (ulSum>= kKRHashPrime)
159 ulSum -= kKRHashPrime;
160 }
161 }
162
163 return (ulSum + 1);
164}
165
166
167////////////////////////////////////////////////////////////////////////
168// IsMainThreadMT [extern, exported]
169//
170// Returns TRUE if the calling thread is the main MFC thread represented
171// by CWinApp.
172////////////////////////////////////////////////////////////////////////
173BOOL IsMainThreadMT()
174{
175 ASSERT(::AfxGetApp()->m_nThreadID);
176 return (::GetCurrentThreadId() == ::AfxGetApp()->m_nThreadID);
177}
178
179
180////////////////////////////////////////////////////////////////////////
181// TimeDateStringMT [extern, exported]
182//
183////////////////////////////////////////////////////////////////////////
184char* TimeDateStringMT(char* pszBuffer, long lSeconds, BOOL bDoDate)
185{
186 return TimeDateStringFormatMT(pszBuffer, lSeconds, 0, bDoDate ? "%1 %2" : "%1");
187}
188
189
190////////////////////////////////////////////////////////////////////////
191// TimeDateStringFormatMT [extern, exported]
192//
193// Produces a time/date string in a specified format.
194// Format can contain the following parameters:
195// %1 Time
196// %2 Date
197// %3 Day of week
198// %4 Timezone
199// Caller should pass in at least a 128-byte buffer
200////////////////////////////////////////////////////////////////////////
201char* TimeDateStringFormatMT(char* buf, long Seconds, int TimeZoneMinutes, const char* Format)
202{
203 char Time[32];
204 char Date[32];
205 char DayOfWeek[32];
206 char TimeZone[32];
207 const char* Args[4] = {Time, Date, DayOfWeek, TimeZone};
208
209 static bool bWasInited = false;
210 static char iTime[3];
211 static char sTime[3];
212 static char s1159[8];
213 static char s2359[8];
214 static char sShortDate[32];
215 if (!bWasInited)
216 {
217 if (::GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ITIME, iTime, sizeof(iTime)) == 0)
218 {
219 strcpy(iTime, "0");
220 }
221
222 if (::GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STIME, sTime, sizeof(sTime)) == 0)
223 {
224 strcpy(sTime, ":");
225 }
226
227 if (::GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_S1159, s1159, sizeof(s1159)) == 0)
228 {
229 strcpy(s1159, "AM");
230 }
231
232 if (::GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_S2359, s2359, sizeof(s2359)) == 0)
233 {
234 strcpy(s2359, "PM");
235 }
236
237 if (::GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, sShortDate, sizeof(sShortDate)) == 0)
238 {
239 strcpy(sShortDate, "m/d/yy");
240 }
241
242 bWasInited = true;
243 }
244
245 if (Seconds < 0)
246 {
247 Seconds = 1;
248 }
249
250 struct tm *TheTime = _localtime32((const __time32_t *)&Seconds);
251
252 // Find out types are in the format string
253 bool bHasTime = false, bHasDate = false, bHasDayOfWeek = false, bHasTimeZone = false;
254
255 for (const char* f = Format; *f; )
256 {
257 if (*f++ == '%')
258 {
259 switch (*f)
260 {
261 case '1': bHasTime = true; break;
262 case '2': bHasDate = true; break;
263 case '3': bHasDayOfWeek = true; break;
264 case '4': bHasTimeZone = true; break;
265 }
266 f++;
267 }
268 }
269
270
271 if (bHasDate)
272 {
273 char *d = Date;
274 int count;
275 for (const char *s = sShortDate; *s; s++)
276 {
277 switch (*s)
278 {
279 case 'M':
280 case 'm':
281 count = 1;
282 while (s[1] == 'M' || s[1] == 'm')
283 {
284 count++;
285 s++;
286 }
287 d += sprintf(d, (count < 2? "%d" : "%02d"), TheTime->tm_mon + 1);
288 break;
289
290 case 'D':
291 case 'd':
292 count = 1;
293 while (s[1] == 'D' || s[1] == 'd')
294 {
295 count++;
296 s++;
297 }
298 d += sprintf(d, (count < 2? "%d" : "%02d"), TheTime->tm_mday);
299 break;
300
301 case 'Y':
302 case 'y':
303 count = 1;
304 while (s[1] == 'Y' || s[1] == 'y')
305 {
306 count++;
307 s++;
308 }
309 d += sprintf(d, (count < 4? "%02d" : "%04d"),
310 (count < 4? TheTime->tm_year % 100 : TheTime->tm_year + 1900));
311 break;
312
313 default:
314 *d++ = *s;
315 }
316 }
317
318 // Optimization, as most dates will just be ANCIENT format
319 if (strcmp(Format, "%2") == 0)
320 {
321 strcpy(buf, Date);
322 return (buf);
323 }
324 }
325
326 if (bHasTime)
327 {
328 int hour = TheTime->tm_hour;
329 if ('0' == *iTime)
330 {
331 hour %= 12;
332 if (hour == 0)
333 hour = 12;
334 }
335 char *AmPm = "";
336 if ('0' == *iTime)
337 AmPm = TheTime->tm_hour < 12? s1159 : s2359;
338 sprintf(Time, "%02d%s%02d%s%s", hour, sTime, TheTime->tm_min, ('0' == *iTime? " " : ""), AmPm);
339 }
340
341 if (bHasDayOfWeek)
342 {
343 strftime(DayOfWeek, sizeof(DayOfWeek), "%A", TheTime);
344 }
345
346 if (bHasTimeZone)
347 {
348 if (TimeZoneMinutes == INT_MAX)
349 TimeZone[0] = 0;
350 else
351 sprintf(TimeZone, "%+2.2d%2.2d", TimeZoneMinutes / 60, abs(TimeZoneMinutes) % 60);
352 }
353
354 {
355 CStringA Result;
356 ::AfxFormatStrings(Result, Format, Args, 4);
357 Result.TrimRight(); // remove trailing whitespace
358 int len = Result.GetLength();
359 if (len > 63) len = 63; // 63 makes it not crash
360 strncpy(buf, Result, len);
361 buf[len] = 0;
362 return (buf);
363 }
364}
365
366
367////////////////////////////////////////////////////////////////////////
368// FormatTimeMT [extern, exported]
369//
370////////////////////////////////////////////////////////////////////////
371CString FormatTimeMT( __time32_t theTime, const char * pFormat )
372{
373 char szTimeBuffer[128];
374
375 struct tm* ptmTemp = _localtime32( &theTime );
376 ASSERT(ptmTemp != NULL); // make sure the time has been initialized!
377
378 if (0 == strftime(szTimeBuffer, sizeof(szTimeBuffer), pFormat, ptmTemp))
379 {
380 szTimeBuffer[0] = '\0';
381 }
382
383 return szTimeBuffer;
384}
385
386
387////////////////////////////////////////////////////////////////////////
388// IsDaylightSavingsTimeMT [static]
389//
390// Zortech C++ compiler doesn't figure Daylight Savings Time, so we
391// have to do it ourselves.
392//
393// Daylight Savings Time changes the first Sunday in April and
394// the last Sunday in October.
395////////////////////////////////////////////////////////////////////////
396static BOOL IsDaylightSavingsTimeMT()
397{
398 time_t curTime = time(NULL);
399 struct tm* pTimeInfo = localtime(&curTime);
400 if (NULL == pTimeInfo)
401 return FALSE;
402
403 int nMonth = pTimeInfo->tm_mon;
404
405 if (nMonth < 3 || nMonth > 9)
406 return FALSE; // Months 0 - 2, and 10 - 11 are always not DST
407 if (nMonth > 3 && nMonth < 9)
408 return TRUE; // Months 4 - 8 are always DST
409
410 // Check out an April date
411 if (nMonth == 3)
412 {
413 // See if there's a Sunday in the month before (and including) today
414 if (pTimeInfo->tm_wday - pTimeInfo->tm_mday < 0)
415 return TRUE; // found one, so it is DST
416 return FALSE; // before first Sunday, so not DST
417 }
418
419 // Check out an October date
420 // See if there's a Sunday in the month after today
421 int nDaysLeft = 31 - pTimeInfo->tm_mday - 1;
422 if (pTimeInfo->tm_wday + nDaysLeft > 6)
423 return TRUE; // found one, so still DST
424 return FALSE; // past last Sunday, so now we're not DST
425}
426
427
428////////////////////////////////////////////////////////////////////////
429// GetGMTOffsetMT [extern]
430//
431// Find the time zone offset from GMT of this PC. FORNOW, this doesn't
432// really belong here anymore...
433////////////////////////////////////////////////////////////////////////
434HRESULT GetGMTOffsetMT(const char* pszTimeZone, int* pOffset)
435{
436 if (NULL == pszTimeZone || NULL == pOffset)
437 {
438 ASSERT(0);
439 return E_INVALIDARG;
440 }
441
442 static int nGMTOffset = -1;
443 static int LastHourChecked = -1;
444 static int LastDayChecked = -1;
445 time_t Now = time(NULL);
446 const struct tm* TimeStruct = localtime(&Now);
447
448 if (nGMTOffset != -1)
449 {
450 if (TimeStruct && TimeStruct->tm_hour == LastHourChecked && TimeStruct->tm_mday == LastDayChecked)
451 {
452 // We've already done the calculation in this hour, so short circuit
453 // the calculation and return the previously computed result.
454 *pOffset = nGMTOffset;
455 return S_OK;
456 }
457 }
458
459 if (TimeStruct)
460 {
461 LastHourChecked = TimeStruct->tm_hour;
462 LastDayChecked = TimeStruct->tm_mday;
463 }
464
465 char szTimeZone[64] = { '\0' };
466 strncpy(szTimeZone, pszTimeZone, sizeof(szTimeZone));
467 szTimeZone[sizeof(szTimeZone) - 1] = '\0'; // just in case
468
469 if (!*szTimeZone)
470 {
471 char szBuffer[64];
472 if (::GetEnvironmentVariableA("TZ", szBuffer, sizeof(szBuffer)))
473 {
474 //
475 // TimeZone INI setting not set, so use TZ environment variable.
476 //
477 strcpy(szTimeZone, szBuffer);
478 }
479 else
480 {
481 //
482 // TimeZone INI setting and TZ environment variable are both
483 // not set, so see if the system can figure it out.
484 //
485 TIME_ZONE_INFORMATION tzi;
486 switch (::GetTimeZoneInformation(&tzi))
487 {
488 case TIME_ZONE_ID_STANDARD:
489 *pOffset = tzi.Bias + tzi.StandardBias;
490 break;
491 case TIME_ZONE_ID_DAYLIGHT:
492 *pOffset = tzi.Bias + tzi.DaylightBias;
493 break;
494 case TIME_ZONE_ID_UNKNOWN:
495 // Weird case. Under NT, if the current timezone doesn't change time
496 // for Daylight Savings, then GetTimeZoneInforamtion() returns
497 // TIME_ZONE_ID_UNKNOWN. Win 95 returns TIME_ZONE_ID_STANDARD, so it
498 // will get correctly handled above and StandardBias will be zero.
499 OSVERSIONINFO OSInfo;
500 ZeroMemory(&OSInfo, sizeof(OSInfo));
501 OSInfo.dwOSVersionInfoSize = sizeof(OSInfo);
502 if (GetVersionEx(&OSInfo) && OSInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
503 {
504 *pOffset = tzi.Bias;
505 break;
506 }
507 // fall through to default case
508 default:
509 // Don't know, so just use a zero offset
510 ASSERT(0);
511 *pOffset = 0;
512 }
513
514 nGMTOffset = *pOffset;
515 return S_OK;
516 }
517 }
518
519 //
520 // Parse the time zone from either the TimeZone INI setting or the TZ system environment variable.
521 //
522 TrimWhitespaceMT(szTimeZone);
523 if (strlen(szTimeZone) < 4)
524 {
525 *pOffset = 0;
526 return E_FAIL;
527 }
528
529 char* pszZone = szTimeZone;
530 while (*pszZone && !isdigit((int)(unsigned char)*pszZone) && *pszZone != '-' && *pszZone != '+')
531 pszZone++;
532 if (!*pszZone)
533 {
534 *pOffset = 0;
535 return E_FAIL;
536 }
537 nGMTOffset = atoi(pszZone);
538 if (*pszZone == '-' || *pszZone == '+')
539 pszZone++;
540 if (!isdigit((int)(unsigned char)*pszZone))
541 {
542 *pOffset = 0;
543 return E_FAIL;
544 }
545 while (isdigit((int)(unsigned char)*pszZone))
546 pszZone++;
547
548 nGMTOffset *= 60;
549 if (*pszZone && IsDaylightSavingsTimeMT())
550 nGMTOffset -= 60;
551 if (nGMTOffset == -24*60)
552 nGMTOffset = 0;
553 else if (nGMTOffset < -23*60 || nGMTOffset > 23*60)
554 {
555 *pOffset = 0;
556 return E_FAIL;
557 }
558
559 *pOffset = nGMTOffset;
560 return S_OK;
561}
562
563
564////////////////////////////////////////////////////////////////////////
565// CSortedStringList::Add [public]
566//
567////////////////////////////////////////////////////////////////////////
568void CSortedStringListMT::Add( LPCSTR lpString ) // does a sorted insert
569{
570 CString str(lpString);
571 Add(str);
572}
573
574
575////////////////////////////////////////////////////////////////////////
576// CSortedStringList::Add [public]
577//
578////////////////////////////////////////////////////////////////////////
579void CSortedStringListMT::Add(
580 CString & aString ) // inserts a string into the list in sorted order
581{
582 if ( IsEmpty() )
583 {
584 AddHead( aString );
585 }
586 else
587 {
588 // I fixed a serious bug here. 'prevpos' was originally initialized to NULL.
589 // It should be initialized to the head position. Pete Maclean 19-Mar-2020.
590 POSITION pos = GetHeadPosition();
591 POSITION prevpos = pos;
592
593 while ( pos )
594 {
595 CString & node = GetNext( pos );
596
597 if ( node > aString )
598 {
599 InsertBefore( prevpos, aString );
600 return;
601 }
602
603 prevpos = pos;
604 }
605
606 // still not added
607 AddTail( aString );
608 }
609}
610
611
612//FORNOWvoid CSortedStringList::Dump( void )
613//FORNOW{
614//FORNOW POSITION pos;
615//FORNOW
616//FORNOW pos = GetHeadPosition();
617//FORNOW
618//FORNOW while ( pos )
619//FORNOW {
620//FORNOW CString & node = GetNext( pos );
621//FORNOW
622//FORNOW AfxMessageBox( node );
623//FORNOW }
624//FORNOW}
625
626
627////////////////////////////////////////////////////////////////////////
628// SafeStrdupMT [extern, exported]
629//
630// A safe form of strdup that checks for NULL pointers,
631// and uses new instead of malloc.
632////////////////////////////////////////////////////////////////////////
633char* SafeStrdupMT(const char* pszString)
634{
635 if (NULL == pszString)
636 return NULL;
637
638 char* pszNewString = DEBUG_NEW_NOTHROW char[strlen(pszString) + 1];
639 if (pszNewString != NULL)
640 return strcpy(pszNewString, pszString);
641
642 return NULL;
643}
644
645
646////////////////////////////////////////////////////////////////////////
647// SafeStrlenMT [extern, exported]
648//
649////////////////////////////////////////////////////////////////////////
650size_t SafeStrlenMT(const char* pszString)
651{
652 return (pszString ? strlen(pszString) : 0);
653}
654
655
656////////////////////////////////////////////////////////////////////////
657// TrimWhitespaceMT [extern, exported]
658//
659// Gets rid of white space at the beginning and end of a string.
660////////////////////////////////////////////////////////////////////////
661char* TrimWhitespaceMT(char* pszBuffer)
662{
663 char* pszEnd;
664 if (!pszBuffer || !*pszBuffer)
665 return pszBuffer;
666
667 //
668 // Seek to first non-space character.
669 //
670 char *pszBufPtr = pszBuffer;
671 while (pszBufPtr && isspace((int)(unsigned char)*pszBufPtr))
672 {
673 pszBufPtr++;
674 }
675
676 //
677 // Save start character.
678 //
679 char *pszBegin = pszBufPtr++;
680
681 for (pszEnd = pszBufPtr; *pszBufPtr; pszBufPtr++)
682 {
683 if (!isspace((int)(unsigned char)*pszBufPtr))
684 {
685 pszEnd = pszBufPtr;
686 }
687 }
688
689 // if buf is 1 char long and 'pszEnd' is a WS, terminate it
690 if (*pszEnd && isspace((int)(unsigned char)*pszEnd))
691 {
692 *pszEnd = '\0';
693 }
694 else if (*pszEnd)
695 {
696 pszEnd[1] = '\0';
697 }
698
699 //
700 // Copy trimmed string in-place, if necessary.
701 //
702 if (pszBegin != pszBuffer)
703 {
704 strcpy(pszBuffer, pszBegin);
705 }
706
707 return pszBuffer;
708}
709
710
711////////////////////////////////////////////////////////////////////////
712// TrimWhitespaceMT [extern, exported]
713//
714// Gets rid of white space at the beginning and end of a string
715////////////////////////////////////////////////////////////////////////
716const char* TrimWhitespaceMT(CString& cs)
717{
718 TrimWhitespaceMT(cs.GetBuffer(cs.GetLength()));
719 cs.ReleaseBuffer();
720 return cs;
721}
722
723
724////////////////////////////////////////////////////////////////////////
725// RemovePrefixMT [extern, exported]
726//
727// Routine to get rid of prefixes. Handy for ditching Re: part of subjects.
728////////////////////////////////////////////////////////////////////////
729const char* RemovePrefixMT(const char* Prefix, const char* String)
730{
731 ASSERT(Prefix && *Prefix); // Bozo!
732
733 if (!String)
734 return (String);
735
736 const char* s = String;
737 for (; *Prefix && *s; s++)
738 {
739 if (tolower(*s) == tolower(*Prefix))
740 Prefix++;
741 else
742 {
743 if (isalpha((unsigned char)*s))
744 return (String);
745 }
746 }
747
748 // If we didn't match the whole prefix, then it's not a match
749 if (*Prefix)
750 return (String);
751
752 while (isspace((int)(unsigned char)*s))
753 s++;
754
755 return (s);
756}
757
758//
759// RemoveSubjectPrefixMT [extern, exported]
760//
761// Removes appropriate subject prefixes -- quickly.
762//
763const char *RemoveSubjectPrefixMT(const char *pSubjectStr)
764{
765 const char cDelim = ':';
766 const char *pSave;
767
768 if (pSubjectStr)
769 {
770 while (*pSubjectStr)
771 {
772 pSave = pSubjectStr;
773
774 // Look for "Re"
775 if ((pSubjectStr[0] == 'R' || pSubjectStr[0] == 'r') &&
776 (pSubjectStr[1] == 'E' || pSubjectStr[1] == 'e'))
777 {
778 pSubjectStr += 2;
779 }
780 // Look for "Fw" or "Fwd"
781 else if ((pSubjectStr[0] == 'F' || pSubjectStr[0] == 'f') &&
782 (pSubjectStr[1] == 'W' || pSubjectStr[1] == 'w'))
783 {
784 pSubjectStr += 2;
785 if (pSubjectStr[0] == 'D' || pSubjectStr[0] == 'd')
786 pSubjectStr += 1;
787 }
788 else
789 break; // Niether prefix, so we are done
790
791 // Now skip non-alpha chars til we hit the deliminator char
792 while ((*pSubjectStr) && (*pSubjectStr != cDelim) && (!isalpha((unsigned char)*pSubjectStr)))
793 pSubjectStr++;
794
795 // If we hit an alpha or end before the delim, then
796 // we back up to before the prefix and we are done
797 if (*pSubjectStr != cDelim)
798 return (pSave);
799
800 // Skip the delim
801 pSubjectStr++;
802
803 // Skip whitespace after the delim
804 while (isspace((unsigned char)*pSubjectStr))
805 pSubjectStr++;
806 }
807 }
808
809 return (pSubjectStr);
810}
811
812////////////////////////////////////////////////////////////////////////
813// SwapShortMT [extern, exported]
814//
815// Byte swapping routine for 16-bit quantities.
816////////////////////////////////////////////////////////////////////////
817unsigned short SwapShortMT(unsigned short sword)
818{
819 ASSERT(sizeof(unsigned short) == 2);
820
821#pragma warning(disable : 4244)
822 // 32 bit compiler will use an unsigned integer for the math
823 unsigned int t = ((unsigned int)sword) & 0x00FF;
824 sword = (unsigned short)(((unsigned int)sword >> 8) & 0x00FF) | ((t << 8) & 0xFF00);
825 return (sword);
826#pragma warning(default : 4244)
827}
828
829
830////////////////////////////////////////////////////////////////////////
831// SwapLongMT [extern, exported]
832//
833// Byte swapping routine for 32-bit quantities.
834////////////////////////////////////////////////////////////////////////
835unsigned long SwapLongMT(unsigned long lword)
836{
837 ASSERT(sizeof(unsigned long) == 4);
838
839 unsigned long t = lword & 0x0000FFFF;
840 lword = (lword >> 16) & 0x0000FFFF;
841 t = SwapShortMT((unsigned short) t);
842 lword = SwapShortMT((unsigned short) lword);
843
844 lword = lword | ((t << 16) & 0xFFFF0000);
845
846 return (lword);
847}
848
849
850bool IsVersionGreaterThanOSR2()
851{
852 OSVERSIONINFOA osInfo;
853 osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
854 if(!::GetVersionExA(&osInfo))
855 return false;
856
857 //if os is greater than win95 OSR2
858 if( (osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
859 (osInfo.dwMinorVersion > 0 || osInfo.dwBuildNumber > 1000) )
860 return true;
861
862 if( (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
863 (osInfo.dwMajorVersion >= 4) )
864 return true;
865
866 return false;
867}
868
869
870
871////////////////////////////////////////////////////////////////////////
872// GetAvailableSpaceMT [extern, exported]
873//
874////////////////////////////////////////////////////////////////////////
875DWORD GetAvailableSpaceMT(const char* pszDrive)
876{
877 DWORD dwTotBytes = 0xFFFFFFFFL; // very large by default
878 ASSERT( pszDrive && *pszDrive );
879
880 // pszDrive should be the drive letter, case-insensitive OR UNC drive path
881 // It doesn't matter what follows (Root can point to a path like EudoraDir)
882 char szDrive[ _MAX_PATH ];
883 strncpy( szDrive, pszDrive, _MAX_PATH - 1 );
884 szDrive[ _MAX_PATH - 1 ] = '\0';
885
886 if ( strlen( szDrive ) < 3 )
887 return dwTotBytes;
888
889 if ( szDrive[ 1 ] == ':' )
890 {
891 szDrive[ 3 ] = '\0';
892 strupr( szDrive );
893 }
894 else if ( strstr( szDrive, "\\\\" ) == szDrive )
895 {
896 // UNCs are \\machine\sharename\ - find end of machine name
897 char * end = strchr( &szDrive[ 2 ], '\\' );
898 if ( end )
899 {
900 // now find the end of the sharename
901 end = strchr( end + 1, '\\' );
902 if ( end )
903 *(end + 1) = '\0';
904 }
905 }
906 else
907 return dwTotBytes; // bad path
908
909 // see if we have enough disk space
910 {
911 DWORD SectorsPerCluster;
912 DWORD BytesPerSector;
913 DWORD FreeClusters;
914 DWORD TotalClusters;
915
916 ULARGE_INTEGER FreeBytesAvailableToCaller;
917 ULARGE_INTEGER TotalNumberOfBytes;
918 ULARGE_INTEGER TotalNumberOfFreeBytes;
919
920
921 // smohanty: 04/24/98
922 //
923 // BEGIN
924 //
925 // GetDiskFreeSpaceEx is a function in Kernel32.dll in NT and >=Win95 OSR2.
926 // The code here used to call GetDiskFreeSpaceEx() which would obviously cause the
927 // run-time loader to fail on a <Win95 OSR2 since this function does not exist in
928 // those versions versions of Kernel32.dll. The fix here is to call GetDiskFreeSpaceEx()
929 // through the address obtained by GetProcAddress(). This means that GetDisFreeSpaceEx()
930 // never gets called directly and the run-time loader will merrily load away.
931
932 HINSTANCE hKernel32Inst = ::GetModuleHandleW(L"KERNEL32.DLL");
933
934 if (hKernel32Inst) {
935 // But GetDiskFreeSpaceEx is a macro. So, we have to call the right one.
936 const char *pGetDiskFreeSpaceEx =
937#ifdef UNICODE
938 "GetDiskFreeSpaceExW";
939#else
940 "GetDiskFreeSpaceExA";
941#endif
942 FARPROC fProcGetDiskFreeSpaceEx = ::GetProcAddress(hKernel32Inst, pGetDiskFreeSpaceEx);
943 if (fProcGetDiskFreeSpaceEx != NULL) {
944 typedef BOOL (__stdcall *IndirectGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER,
945 PULARGE_INTEGER);
946 IndirectGetDiskFreeSpaceEx pFn = (IndirectGetDiskFreeSpaceEx)(fProcGetDiskFreeSpaceEx);
947 if ((*pFn)(szDrive, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)) {
948 dwTotBytes = (TotalNumberOfFreeBytes.HighPart > 0) ? 0xFFFFFFFFL : TotalNumberOfFreeBytes.LowPart;
949 }
950 }
951 else { // Could not get the address of GetDiskFreeSpaceEx --- revert back to GetDiskFreeSpace
952 if ( ::GetDiskFreeSpace( szDrive, &SectorsPerCluster, &BytesPerSector, &FreeClusters, &TotalClusters ) ) {
953 dwTotBytes = FreeClusters * SectorsPerCluster * BytesPerSector;
954 }
955 }
956 }
957 else { // Could not load Kernel32.dll. Well, how the hell did the person boot his machine?
958 ASSERT(0);
959 }
960 // smohanty: 04/24/98
961 //
962 // END
963 }
964
965 return dwTotBytes;
966}
967
968
969////////////////////////////////////////////////////////////////////////
970// SetFileExtensionMT [extern, exported]
971//
972////////////////////////////////////////////////////////////////////////
973char* SetFileExtensionMT(char* pszFilename, const char* pszExtension)
974{
975 if (!pszFilename || !pszExtension)
976 return (pszFilename);
977
978 char* p = strrchr(pszFilename, '\\');
979 if (!p)
980 p = pszFilename;
981 if ((p = strrchr(p, '.')) != NULL)
982 p[1] = 0;
983 else
984 strcat(pszFilename, ".");
985
986 if (*pszExtension == '.')
987 pszExtension++;
988
989 return (strcat(pszFilename, pszExtension));
990}
991
992
993////////////////////////////////////////////////////////////////////////
994// StripIllegalShortMT [static]
995//
996////////////////////////////////////////////////////////////////////////
997static char* StripIllegalShortMT(char* pszFilename)
998{
999 //
1000 // Read-only, therefore this is thread safe.
1001 //
1002 static const char s_szIllegalCharShort[] = ". \t\r\n\"*+,/:;<=>?[\\]|";
1003
1004 if (!pszFilename)
1005 return (NULL);
1006
1007 char* s;
1008 char* t;
1009
1010 s = t = pszFilename;
1011 while (1)
1012 {
1013 int len = strcspn(s, s_szIllegalCharShort);
1014 if (len)
1015 {
1016 strncpy(t, s, len);
1017 t += len;
1018 }
1019 s += len;
1020 if (!*s)
1021 break;
1022 s++;
1023 }
1024 *t = 0;
1025
1026 return (pszFilename);
1027}
1028
1029
1030////////////////////////////////////////////////////////////////////////
1031// StripIllegalMT [extern, exported]
1032//
1033////////////////////////////////////////////////////////////////////////
1034char* StripIllegalMT(char* pszFilename, const CString& strTargetDirectory)
1035{
1036 char* szSrc;
1037 char* szDest;
1038
1039 if( !pszFilename )
1040 {
1041 return NULL;
1042 }
1043
1044 if( ! LongFileSupportMT( strTargetDirectory ) )
1045 {
1046 return StripIllegalShortMT( pszFilename );
1047 }
1048
1049 szSrc = szDest = pszFilename;
1050
1051 while( *szSrc )
1052 {
1053 if( ( *szSrc == 32 ) ||
1054 ( *szSrc > 127 ) ||
1055 ( ( *szSrc >= 'a' ) && ( *szSrc <= 'z' ) ) ||
1056 ( ( *szSrc >= 'A' ) && ( *szSrc <= 'Z' ) ) ||
1057 ( ( *szSrc >= '0' ) && ( *szSrc <= '9' ) ) ||
1058 ( strchr( "$%'-_@~`!(){}^#&+,;=[]", *szSrc ) != NULL ) )
1059 {
1060 *szDest = *szSrc;
1061 szDest++;
1062 }
1063 szSrc++;
1064 }
1065
1066 *szDest = 0;
1067
1068 return (pszFilename);
1069}
1070
1071
1072////////////////////////////////////////////////////////////////////////
1073// FileExistsMT [extern, exported]
1074//
1075////////////////////////////////////////////////////////////////////////
1076BOOL FileExistsMT(const char* pszFilename, BOOL IsWritable /*= FALSE*/)
1077{
1078 if (!pszFilename || !*pszFilename)
1079 {
1080 ASSERT(0);
1081 return FALSE;
1082 }
1083
1084 if (access(pszFilename, IsWritable? 2 : 0) == 0)
1085 return TRUE;
1086
1087 return FALSE;
1088}
1089
1090////////////////////////////////////////////////////////////////////////
1091// CreateDirectoryMT [extern, exported]
1092//
1093////////////////////////////////////////////////////////////////////////
1094BOOL CreateDirectoryMT(LPCTSTR DirectoryName)
1095{
1096 return FileExistsMT(DirectoryName, TRUE) || CreateDirectory(DirectoryName, NULL);
1097}
1098
1099////////////////////////////////////////////////////////////////////////
1100// CreateShortcutMT [extern, exported]
1101//
1102////////////////////////////////////////////////////////////////////////
1103HRESULT CreateShortcutMT(LPCTSTR Target, LPCTSTR Link, LPCTSTR Arguments /*= NULL*/, LPCTSTR WorkingDir /*= NULL*/)
1104{
1105 HRESULT hr;
1106 IShellLink* piSL;
1107
1108 hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&piSL);
1109 if (SUCCEEDED(hr))
1110 {
1111 piSL->SetPath(Target);
1112 if (Arguments)
1113 piSL->SetArguments(Arguments);
1114 if (WorkingDir)
1115 piSL->SetWorkingDirectory(WorkingDir);
1116
1117 IPersistFile* piPF;
1118 hr = piSL->QueryInterface(IID_IPersistFile, (LPVOID*)&piPF);
1119
1120 if (SUCCEEDED(hr))
1121 {
1122 CString FullLink(Link);
1123
1124 // If Link ends in backslash, that means it is a directory that
1125 // should hold the link and use the name of the target file
1126 if (FullLink[FullLink.GetLength() - 1] == '\\')
1127 FullLink += _tcsrchr(Target, '\\') + 1;
1128
1129 CString Temp = FullLink;
1130 Temp.MakeLower();
1131 if (Temp.Find(".lnk") == -1)
1132 FullLink += ".lnk";
1133 WORD wsz[MAX_PATH];
1134 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, FullLink, -1, (LPWSTR) wsz, MAX_PATH);
1135
1136 hr = piPF->Save((LPCOLESTR) wsz, TRUE);
1137
1138 piPF->Release();
1139 }
1140 piSL->Release();
1141 }
1142
1143 return hr;
1144}
1145
1146
1147void
1148GetFileNameAndParentPath(
1149 const char * in_szFullPath,
1150 char * out_szParentPathBuffer,
1151 int in_nParentPathBufferSize,
1152 CString * out_szFileName)
1153{
1154 // Get the full path to the parent directory for the old file name
1155 LPTSTR lpszFileName = NULL;
1156 ::GetFullPathNameA(in_szFullPath, in_nParentPathBufferSize, out_szParentPathBuffer, &lpszFileName);
1157
1158 // If the caller wanted the file name copy it before NULL terminating
1159 if (out_szFileName != NULL)
1160 {
1161 *out_szFileName = lpszFileName;
1162 }
1163
1164 // Nul-terminate so that in_nParentPathBufferSize is just the path
1165 if (lpszFileName != NULL)
1166 {
1167 *lpszFileName = '\0';
1168 }
1169}
1170
1171
1172HRESULT
1173MoveFileMaintainingCorrectPermissions(
1174 const char * in_szOldName,
1175 const char * in_szNewName,
1176 DWORD in_dwFileSize,
1177 DWORD in_dwFreeSpaceDestinationVolume)
1178{
1179 // Start by assuming failure
1180 HRESULT hr = MAKE_HRESULT(1, FACILITY_ITF, QCUTIL_E_FILE_RENAME);
1181
1182 // Get the full path to the parent directory for the old file name
1183 TCHAR szOldFilePath[_MAX_PATH];
1184 GetFileNameAndParentPath(in_szOldName, szOldFilePath, _MAX_PATH);
1185
1186 // Get the full path to the parent directory for the new file name
1187 TCHAR szNewFilePath[_MAX_PATH];
1188 GetFileNameAndParentPath(in_szNewName, szNewFilePath, _MAX_PATH);
1189
1190 // Determine if they're inside the same directory
1191 bool bInsideSameDirectory = (stricmp(szOldFilePath, szNewFilePath) == 0);
1192 if (!bInsideSameDirectory)
1193 {
1194 // Pad the file size by the minimum free bytes that we'd like to avoid
1195 // falling below.
1196 in_dwFileSize += kMinExtraFreeSpace;
1197
1198 if (in_dwFileSize < in_dwFreeSpaceDestinationVolume)
1199 {
1200 // In Windows 2000 and XP renaming a file that we created in the temp folder
1201 // to move it into another folder is not ideal. The file created in the temp
1202 // folder is considered private to the current user - no other users have
1203 // rights to files created in that folder. Renaming the file to move it
1204 // into the proper Eudora directory will not change the default permissions
1205 // to reflect the new parent directory.
1206 //
1207 // We'll attempt to copy the file to the new name. This is slower, but it's
1208 // the only way to make sure that the default permissions are set up correctly.
1209 if ( ::CopyFileA(in_szOldName, in_szNewName, TRUE) )
1210 {
1211 // CopyFile worked.
1212 hr = S_OK;
1213
1214 // Remove the original.
1215 FileRemoveMT(in_szOldName);
1216 }
1217 else
1218 {
1219 SetLastErrorResult(&hr);
1220 }
1221 }
1222 }
1223
1224 if ( FAILED(hr) )
1225 {
1226 // Either inside the same directory (so permissions should already
1227 // correct) or failed to copy the file (perhaps because there was
1228 // too little space).
1229 if ( ::MoveFileA(in_szOldName, in_szNewName) )
1230 {
1231 // MoveFile worked.
1232 hr = S_OK;
1233 }
1234 else
1235 {
1236 SetLastErrorResult(&hr);
1237 }
1238 }
1239
1240 return hr;
1241}
1242
1243void
1244SetLastErrorResult(
1245 HRESULT* phr)
1246{
1247 HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
1248 if (FAILED(hr))
1249 *phr = hr;
1250}
1251
1252
1253////////////////////////////////////////////////////////////////////////
1254// FileRenameMT [extern, exported]
1255//
1256// Deprecated. Eventually remove in favor of FileRenameReplaceMT.
1257////////////////////////////////////////////////////////////////////////
1258HRESULT FileRenameMT(const char* pszOldName, const char* pszNewName)
1259{
1260 HRESULT hr = MAKE_HRESULT(1, FACILITY_ITF, QCUTIL_E_FILE_RENAME);
1261
1262 if (!pszOldName || !pszNewName)
1263 {
1264 ASSERT(0);
1265 }
1266 else if (!::MoveFileA(pszOldName, pszNewName))
1267 {
1268 SetLastErrorResult(&hr);
1269 }
1270 else
1271 {
1272 hr = S_OK;
1273 }
1274
1275 return hr;
1276}
1277
1278
1279////////////////////////////////////////////////////////////////////////
1280// FileRenameReplaceMT [extern, exported]
1281//
1282// Renames file from old file name to new file name, optionally
1283// replacing existing file.
1284//
1285// Key features:
1286// * When not replacing (i.e. when being used like old FileRename),
1287// FileRenameReplaceMT maintains default file attributes and
1288// permissions by copying the file into the destination directory
1289// (when necessary - not done when already in same directory).
1290// * When replacing correctly maintains all original file information
1291// when possible (i.e. creation date, attributes, permissions)
1292// * Maintains original file if rename/replace fails
1293////////////////////////////////////////////////////////////////////////
1294HRESULT FileRenameReplaceMT(const char * in_szOldName, const char * in_szNewName, BOOL in_bReplace /*= TRUE*/)
1295{
1296 if (!in_szOldName || !in_szNewName)
1297 {
1298 ASSERT(0);
1299 return MAKE_HRESULT(1, FACILITY_ITF, QCUTIL_E_FILE_RENAME);
1300 }
1301
1302 HRESULT hr = S_OK;
1303
1304 // Get the file size and the volume size so that we can make smart
1305 // decisions about whether or not we should even try to copy the file.
1306 CFileStatus fileStatus;
1307
1308 CFile::GetStatus(in_szOldName, fileStatus);
1309
1310 // Statics
1311 static bool s_bInitializedReplaceFile = false;
1312 static IndirectReplaceFile s_pReplaceFile = NULL;
1313
1314 DWORD dwFileSize = static_cast<DWORD>( fileStatus.m_size );
1315 DWORD dwFreeSpaceDestinationVolume = GetAvailableSpaceMT(in_szNewName);
1316
1317 // Check to see if replacing is really needed
1318 if ( in_bReplace && !FileExistsMT(in_szNewName) )
1319 in_bReplace = FALSE;
1320
1321 if (in_bReplace && !s_bInitializedReplaceFile)
1322 {
1323 // First time replacing a file. Attempt to get the newfangled ReplaceFile API,
1324 // which is only available in Windows 2000 and later.
1325 s_bInitializedReplaceFile = true;
1326
1327 // Check to see if Kernel32.dll is available
1328 HMODULE hKernel32 = ::GetModuleHandleA("KERNEL32");
1329 if (hKernel32)
1330 {
1331 // Get the ReplaceFile function from Kernel32.dll (available in Windows 2000 and later).
1332 s_pReplaceFile = reinterpret_cast<IndirectReplaceFile>( ::GetProcAddress(hKernel32, "ReplaceFile") );
1333 }
1334 }
1335
1336 if (!in_bReplace)
1337 {
1338 // Not replacing file - just attempt to rename the file.
1339 hr = MoveFileMaintainingCorrectPermissions(in_szOldName, in_szNewName, dwFileSize, dwFreeSpaceDestinationVolume);
1340 }
1341 else
1342 {
1343 BOOL bWorked = FALSE;
1344
1345 // Break the new file name full path into path and name
1346 TCHAR szNewFilePath[_MAX_PATH];
1347 CString strNewFileName;
1348 GetFileNameAndParentPath(in_szNewName, szNewFilePath, _MAX_PATH, &strNewFileName);
1349
1350 // Get a temp file name that looks like New<something>.tmp
1351 char szTempPath[_MAX_PATH];
1352 hr = ::GetTempFileNameA(szNewFilePath, strNewFileName, 0, szTempPath);
1353 if ( FAILED(hr) )
1354 return hr;
1355
1356 // Keep track of whether or not we copied the file to the destination
1357 // volume in order to be able to use ReplaceFile
1358 bool bCopiedFileToDestVolume = false;
1359 char szDestVolumeTempFile[_MAX_PATH];
1360 const char * szOldName = in_szOldName;
1361
1362 // Do we have the newfangled ReplaceFile API?
1363 if (s_pReplaceFile)
1364 {
1365 bool bSameVolume = (strnicmp(in_szNewName, in_szOldName, 2) == 0);
1366
1367 // Pad the file size by the minimum free bytes that we'd like to avoid
1368 // falling below.
1369 dwFileSize += kMinExtraFreeSpace;
1370
1371 if ( !bSameVolume && (dwFileSize < dwFreeSpaceDestinationVolume) )
1372 {
1373 // We're on a different volume. ReplaceFile won't work across volumes.
1374 // Try copying to the new volume first, because we'd really prefer for
1375 // ReplaceFile to work. It preserves more of the file information.
1376
1377 // Get a temp file name that looks like New<something>.tmp
1378 hr = ::GetTempFileNameA(szNewFilePath, strNewFileName, 0, szDestVolumeTempFile);
1379 if ( SUCCEEDED(hr) )
1380 {
1381 // Attempt to copy the file to the correct volume
1382 if ( ::CopyFileA(in_szOldName, szDestVolumeTempFile, FALSE) )
1383 {
1384 // We succeeded in copying the file to the new volume.
1385 // The temporary copy of the file is now the source for all
1386 // future operations.
1387 szOldName = szDestVolumeTempFile;
1388
1389 // Remember that we made a temporary copy of the file
1390 bCopiedFileToDestVolume = true;
1391
1392 // The source and destination are now on the same volume
1393 bSameVolume = true;
1394 }
1395 }
1396 }
1397
1398 // Remove the file that GetTempFileName created (we kept it around
1399 // until now so that our second call to GetTempFileName would get
1400 // a different file name).
1401 // Remove now before we call ReplaceFile so that ReplaceFile can
1402 // create a backup copy of the original file.
1403 FileRemoveMT(szTempPath);
1404
1405 if (bSameVolume)
1406 {
1407 USES_CONVERSION;
1408
1409 // Try the newfangled ReplaceFile API.
1410 bWorked = s_pReplaceFile( T2W(in_szNewName), T2W(szOldName), T2W(szTempPath),
1411 REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS,
1412 NULL, NULL );
1413
1414 // If it worked, then we no longer need the backup copy of the file
1415 // that ReplaceFile created.
1416 if (bWorked)
1417 FileRemoveMT(szTempPath);
1418 }
1419 }
1420 else
1421 {
1422 // Remove the file that GetTempFileName created (we kept it around
1423 // until now so that our second call to GetTempFileName would get
1424 // a different file name).
1425 FileRemoveMT(szTempPath);
1426 }
1427
1428 // Start over with our error code (either ReplaceFile worked or we'll get
1429 // an error code when we do the old method of replacing a file below).
1430 hr = S_OK;
1431
1432 if (!bWorked)
1433 {
1434 // We're either running on an older version of Windows, or the newfangled
1435 // ReplaceFile API failed us. Revert to the old method of replacing a file.
1436
1437 // Here's the plan for a safe rename:
1438 //
1439 // 1. Rename New to New<something>.tmp
1440 // 2. Rename Old to New
1441 // 3. Delete New<something>.tmp
1442 //
1443 // That way if something goes wrong, we have a copy of the file
1444 // that's going to get blown away as a result of the rename, and
1445 // we avoid the case where we wind up with neither the old nor
1446 // new version of the file.
1447
1448 // 1. Rename New to New<something>.tmp (if it still exists)
1449 if ( !FileExistsMT(in_szNewName) )
1450 {
1451 // We already checked to see if the file we're replacing existed
1452 // at all before doing this code. The only reason why the file
1453 // should be missing at this point is if ReplaceFile already
1454 // renamed the file we're replacing. Verify that this is the case
1455 // and that we're still replacing.
1456 if ( !FileExistsMT(szTempPath) )
1457 {
1458 ASSERT(0);
1459 in_bReplace = FALSE;
1460 }
1461 }
1462 else
1463 {
1464 // Rename New to New<something>.tmp
1465 hr = FileRenameMT(in_szNewName, szTempPath);
1466 if ( FAILED(hr) )
1467 return hr;
1468 }
1469
1470 // 2. Rename Old to New
1471 if ( SUCCEEDED(hr) )
1472 {
1473 // Try to move the file twice
1474 for (int i = 0; i < 2; i++)
1475 {
1476 // Attempt to move the file to the new name, while maintaining
1477 // correct permissions.
1478 hr = MoveFileMaintainingCorrectPermissions(szOldName, in_szNewName, dwFileSize, dwFreeSpaceDestinationVolume);
1479
1480 // If we succeeded, we don't need to try again
1481 if ( SUCCEEDED(hr) )
1482 break;
1483
1484 if (i == 0)
1485 {
1486 // Failed to rename Old to New, maybe New still existed
1487 // (i.e. rename of New to New<something> hasn't completed)?
1488 // If so, then deleting New and trying rename again may work.
1489 FileRemoveMT(in_szNewName);
1490 }
1491 }
1492 }
1493
1494 if (in_bReplace)
1495 {
1496 if ( SUCCEEDED(hr) )
1497 {
1498 // 3. Delete New<something>.tmp
1499 // Rename went ok, so get rid of the copy of the file being replaced.
1500 FileRemoveMT(szTempPath);
1501 }
1502 else // FAILED(hr)
1503 {
1504 // Uh oh, failed to rename after two attempts, restore the destination file now
1505 FileRenameMT(szTempPath, in_szNewName);
1506 }
1507 }
1508 }
1509
1510 if (bCopiedFileToDestVolume)
1511 {
1512 // in_szOldName and in_szNewName specified different volumes.
1513 // We copied the file to the destination volume before calling ReplaceFile
1514 // (because ReplaceFile only works on the same volume). Even if ReplaceFile
1515 // failed, we kept the temporary copy around so that the file would not need
1516 // to be copied from one volume to the other again.
1517 if ( SUCCEEDED(hr) )
1518 {
1519 // We succeeded, so we need to delete the original file now.
1520 FileRemoveMT(in_szOldName);
1521 }
1522 else // FAILED(hr)
1523 {
1524 // We failed, so we need to delete the temporary copy we made to
1525 // the destination volume.
1526 FileRemoveMT(szDestVolumeTempFile);
1527 }
1528 }
1529 }
1530
1531 return hr;
1532}
1533
1534
1535////////////////////////////////////////////////////////////////////////
1536// FileRemoveMT [extern, exported]
1537//
1538////////////////////////////////////////////////////////////////////////
1539HRESULT FileRemoveMT(const char* pszFilename)
1540{
1541 HRESULT hr = MAKE_HRESULT(1, FACILITY_ITF, QCUTIL_E_FILE_DELETE);
1542
1543 if (!pszFilename || !*pszFilename)
1544 {
1545 ASSERT(0);
1546 }
1547 else if (FileExistsMT(pszFilename) && remove(pszFilename) == -1)
1548 {
1549 SetLastErrorResult(&hr);
1550 }
1551 else
1552 {
1553 hr = S_OK;
1554 }
1555
1556 return hr;
1557}
1558
1559
1560////////////////////////////////////////////////////////////////////////
1561// StripQuotesMT [extern, exported]
1562//
1563// Strip off quotes surrounding long filenames
1564////////////////////////////////////////////////////////////////////////
1565void StripQuotesMT(char *pszString)
1566{
1567 if (!(*pszString))
1568 return;
1569
1570 char *pszStart = pszString;
1571 if (*pszStart == '\"')
1572 pszStart++;
1573 char *c = ((char *)pszStart + strlen(pszStart) - 1);
1574 if (*c == '\"')
1575 *c = 0;
1576
1577 strcpy(pszString, pszStart);
1578}
1579
1580
1581////////////////////////////////////////////////////////////////////////
1582// LongFileSupportMT [extern, exported]
1583//
1584// This does all the dirty work to see if we can use long filenames
1585////////////////////////////////////////////////////////////////////////
1586BOOL LongFileSupportMT(const char *pszPathname)
1587{
1588 char szDrive[_MAX_PATH];
1589
1590 if (::strlen(pszPathname) < 3)
1591 return FALSE;
1592
1593 if (pszPathname[1] == ':')
1594 {
1595 szDrive[0] = *pszPathname;
1596 szDrive[1] = ':';
1597 szDrive[2] = '\\';
1598 szDrive[3] = 0;
1599 }
1600 else if (pszPathname[0] == '\\' && pszPathname[1] == '\\')
1601 {
1602 const char* backslash;
1603
1604 if ((backslash = strchr(pszPathname + 2, '\\')) != NULL &&
1605 (backslash = strchr(backslash + 1, '\\')) != NULL)
1606 {
1607 const int len = backslash - pszPathname + 1;
1608 strncpy(szDrive, pszPathname, len);
1609 szDrive[len] = 0;
1610 }
1611 }
1612 else
1613 return FALSE;
1614
1615 char szVolumeName[_MAX_PATH];
1616 char szFileSys[255];
1617 DWORD dwMaxFileName = 0;
1618 DWORD dwFlags = 0;
1619 BOOL bSuccess = ::GetVolumeInformationA(szDrive, szVolumeName, _MAX_PATH, NULL,
1620 &dwMaxFileName, &dwFlags, szFileSys, 255);
1621
1622 if (bSuccess && dwMaxFileName > 12)
1623 return TRUE;
1624 else
1625 return FALSE;
1626}
1627
1628
1629////////////////////////////////////////////////////////////////////////
1630// GetTmpFileNameMT [extern, exported]
1631//
1632////////////////////////////////////////////////////////////////////////
1633CString GetTmpFileNameMT(const char *pszPrefix /*="eud"*/)
1634{
1635 char szPath[_MAX_PATH]; // pathname to TMP directory
1636 ::GetTempPathA(_MAX_PATH, szPath);
1637
1638 char szTempFile[_MAX_PATH]; // create new temp file in TMP directory
1639 ::GetTempFileNameA(szPath, pszPrefix, 0, szTempFile);
1640
1641 return CString(szTempFile);
1642}
1643
1644LONG GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata)
1645{
1646 HKEY hkey;
1647 LONG retval = RegOpenKeyExA(key, subkey, 0, KEY_QUERY_VALUE, &hkey);
1648
1649 if (retval == ERROR_SUCCESS) {
1650 long datasize = MAX_PATH;
1651 TCHAR data[MAX_PATH];
1652 RegQueryValueA(hkey, NULL, data, &datasize);
1653 lstrcpy(retdata,data);
1654 RegCloseKey(hkey);
1655 }
1656
1657 return retval;
1658}
1659
1660HINSTANCE GotoURL(LPCTSTR url, int showcmd)
1661{
1662 TCHAR key[MAX_PATH + MAX_PATH];
1663
1664 // First try ShellExecute()
1665 HINSTANCE result = ShellExecuteA(NULL, _T("open"), url, NULL,NULL, showcmd);
1666
1667 // If it failed, get the .htm regkey and lookup the program
1668 if ((UINT)result <= HINSTANCE_ERROR) {
1669
1670 if (GetRegKey(HKEY_CLASSES_ROOT, _T(".htm"), key) == ERROR_SUCCESS) {
1671 lstrcat(key, _T("\\shell\\open\\command"));
1672
1673 if (GetRegKey(HKEY_CLASSES_ROOT,key,key) == ERROR_SUCCESS) {
1674 TCHAR *pos;
1675 pos = _tcsstr(key, _T("\"%1\""));
1676 if (pos == NULL) { // No quotes found
1677 pos = strstr(key, _T("%1")); // Check for %1, without quotes
1678 if (pos == NULL) // No parameter at all...
1679 pos = key+lstrlen(key)-1;
1680 else
1681 *pos = '\0'; // Remove the parameter
1682 }
1683 else
1684 *pos = '\0'; // Remove the parameter
1685
1686 lstrcat(pos, _T(" "));
1687 lstrcat(pos, url);
1688 result = (HINSTANCE) ::WinExec(key,showcmd);
1689 }
1690 }
1691 }
1692
1693 return result;
1694}
1695
1696// Routines to find if a specific process is executing currently
1697// The reason why functions to find modules are implemented in two ways
1698// is because ToolHelp32 way would work 'only' under Win95/98
1699// and PSAPI.DLL is by default present in NT 3.* and 4.*
1700
1701// Created: 01/05/2000, Apul Nahata
1702
1703// Local Error Codes
1704#define _FP_SUCCESS TRUE
1705#define _FP_ERROR_BASE 8000
1706#define _FP_SYSTEM_DLL_NOT_FOUND _FP_ERROR_BASE + 1
1707#define _FP_QUERIED_MODULE_NOT_FOUND _FP_ERROR_BASE + 2
1708#define _FP_GENERIC_ERROR _FP_ERROR_BASE + 3
1709
1710// NETSCAPE's Most Significant 32 bit value for version 4.05
1711#define _NETSCAPE_405_PRODUCT_VERSION_MS_ 262149 // Hex of 262149 = 40005 i.e 4.05
1712
1713// Function pointers for ToolHelp32 functions
1714typedef HANDLE (WINAPI * PFNCREATETOOLHELP32SNAPSHOT)(DWORD dwFlags, DWORD dwth32ProcessID);
1715typedef BOOL (WINAPI * PFNPROCESS32FIRST)(HANDLE hSnapshot, LPPROCESSENTRY32 lpProcessEntry32);
1716typedef BOOL (WINAPI * PFNPROCESS32NEXT)(HANDLE hSnapshot, LPPROCESSENTRY32 lpProcessEntry32);
1717typedef BOOL (WINAPI * PFNMODULE32FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpModuleEntry32);
1718typedef BOOL (WINAPI * PFNMODULE32NEXT)(HANDLE hSnapshot, LPMODULEENTRY32 lpModuleEntry32);
1719
1720
1721// Function pointers for PSAPI.DLL functions
1722typedef BOOL (WINAPI * PFNENUMPROCESSES)(DWORD *lpidProcess,
1723 DWORD dwCb,
1724 DWORD *dwCbNeeded);
1725
1726typedef BOOL (WINAPI * PFNENUMPROCESSMODULES)(HANDLE hProcess,
1727 HMODULE *lphModule,
1728 DWORD dwCb,
1729 LPDWORD lpcbNeeded);
1730
1731typedef DWORD (WINAPI * PFNGETMODULEFILENAMEEXA)(HANDLE hProcess,
1732 HMODULE hModule,
1733 LPSTR lpFilename,
1734 DWORD dwSize);
1735
1736//Find if the module is loaded using ToolHelp32
1737//DWORD FindifExecutingUsingToolHelp32(LPCTSTR lpszModuleName)
1738DWORD FindifExecutingUsingToolHelp32(LPCTSTR lpszModuleName,DWORD *dwProductVersionMS = NULL, DWORD *dwProductVersionLS = NULL)
1739{
1740 static HMODULE hModKERNEL32 = 0;
1741 static PFNCREATETOOLHELP32SNAPSHOT pfnCreateToolhelp32Snapshot = 0;
1742 static PFNPROCESS32FIRST pfnProcess32First = 0;
1743 static PFNPROCESS32NEXT pfnProcess32Next = 0;
1744 static PFNMODULE32FIRST pfnModule32First = 0;
1745 static PFNMODULE32NEXT pfnModule32Next = 0;
1746
1747 // Hook up to the ToolHelp32 functions dynamically.
1748
1749 if ( NULL == hModKERNEL32 )
1750 hModKERNEL32 = GetModuleHandleW( L"KERNEL32.DLL" );
1751
1752 pfnCreateToolhelp32Snapshot = (PFNCREATETOOLHELP32SNAPSHOT)
1753 GetProcAddress( hModKERNEL32, "CreateToolhelp32Snapshot" );
1754
1755 pfnProcess32First = (PFNPROCESS32FIRST)
1756 GetProcAddress( hModKERNEL32, "Process32First" );
1757
1758 pfnProcess32Next = (PFNPROCESS32NEXT)
1759 GetProcAddress( hModKERNEL32, "Process32Next" );
1760
1761 pfnModule32First = (PFNMODULE32FIRST)
1762 GetProcAddress( hModKERNEL32, "Module32First" );
1763
1764 pfnModule32Next = (PFNMODULE32NEXT)
1765 GetProcAddress( hModKERNEL32, "Module32Next" );
1766
1767 if ( (NULL == pfnCreateToolhelp32Snapshot) ||
1768 (NULL == pfnProcess32First) ||
1769 (NULL == pfnProcess32Next) ||
1770 (NULL == pfnModule32First) ||
1771 (NULL == pfnModule32Next) )
1772 return _FP_SYSTEM_DLL_NOT_FOUND;
1773
1774
1775 // Create a ToolHelp32 snapshot containing the process list
1776 HANDLE hSnapshotProcess;
1777 hSnapshotProcess = pfnCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
1778 if ( NULL == hSnapshotProcess )
1779 return _FP_GENERIC_ERROR;
1780
1781 // Iterate through each of the processes in the snapshot
1782 PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
1783 BOOL bProcessWalkContinue;
1784
1785 for (bProcessWalkContinue = pfnProcess32First(hSnapshotProcess,&processEntry);
1786 bProcessWalkContinue;
1787 bProcessWalkContinue = pfnProcess32Next(hSnapshotProcess,&processEntry) )
1788 {
1789
1790 // Enumerate the module list for this process.
1791
1792 HANDLE hSnapshotModule;
1793 hSnapshotModule = pfnCreateToolhelp32Snapshot( TH32CS_SNAPMODULE,
1794 processEntry.th32ProcessID );
1795 if ( NULL == hSnapshotModule )
1796 continue;
1797
1798 // Iterate through each module in the snapshot
1799 MODULEENTRY32 moduleEntry = { sizeof(MODULEENTRY32) };
1800 BOOL bModuleWalkContinue;
1801
1802 for (bModuleWalkContinue = pfnModule32First(hSnapshotModule,&moduleEntry);
1803 bModuleWalkContinue;
1804 bModuleWalkContinue = pfnModule32Next(hSnapshotModule,&moduleEntry) )
1805 {
1806 // Hack alert! not so intelligent way to figure out if this is EXE module itself
1807 if ( 0 == _tcsicmp( moduleEntry.szExePath, processEntry.szExeFile ) )
1808 {
1809 // Compare to find the actual module.exe here
1810 if (NULL != _tcsstr(_tcslwr(moduleEntry.szExePath),_tcslwr((TCHAR *)lpszModuleName)))
1811 {
1812 DWORD dwHandle = 0;
1813 DWORD dwSize = ::GetFileVersionInfoSizeA((TCHAR *)(const TCHAR *) moduleEntry.szExePath, &dwHandle);
1814 if (NULL == dwSize)
1815 return _FP_GENERIC_ERROR; // EXE doesn't have VERSIONINFO data?
1816
1817
1818 // Allocate data buffers of the proper sizes.
1819 LPSTR pVerData = DEBUG_NEW_NOTHROW TCHAR[dwSize];
1820 if (NULL == pVerData)
1821 return _FP_GENERIC_ERROR;
1822
1823 // Fetching the actual VERSIONINFO data.
1824 if (! ::GetFileVersionInfoA((TCHAR *)(const TCHAR *) moduleEntry.szExePath, dwHandle, dwSize, pVerData))
1825
1826 {
1827 ASSERT(0); // missing VERSIONINFO data?
1828 delete [] pVerData;
1829 return _FP_GENERIC_ERROR;
1830 }
1831
1832 LPSTR pVsFixedFileInfoData = NULL;
1833 if (NULL == (pVsFixedFileInfoData = DEBUG_NEW_NOTHROW TCHAR [sizeof(VS_FIXEDFILEINFO)] ) )
1834 {
1835 delete [] pVerData;
1836 return _FP_GENERIC_ERROR;
1837 }
1838
1839 UINT bufsize = 0;
1840
1841 TCHAR sztmpStr[5];
1842 _tcscpy(sztmpStr, "\\");
1843 if (::VerQueryValueA(pVerData,
1844 sztmpStr,
1845 (void **) &pVsFixedFileInfoData,
1846 &bufsize))
1847 {
1848 if (pVsFixedFileInfoData && bufsize)
1849 {
1850 VS_FIXEDFILEINFO* p_info = (VS_FIXEDFILEINFO *) pVsFixedFileInfoData;
1851 *dwProductVersionMS = p_info->dwProductVersionMS;
1852 *dwProductVersionLS = p_info->dwProductVersionLS;
1853 }
1854 }
1855
1856 delete [] pVerData;
1857 return _FP_SUCCESS;
1858 }
1859
1860 }
1861 }
1862
1863 CloseHandle( hSnapshotModule ); // Done with module list snapshot
1864 }
1865
1866 CloseHandle( hSnapshotProcess ); // Done with process list snapshot
1867
1868 return _FP_QUERIED_MODULE_NOT_FOUND;
1869}
1870
1871//Find if the module is loaded using PSAPI
1872//DWORD FindifExecutingUsingPSAPI(LPCTSTR lpszModuleName)
1873DWORD FindifExecutingUsingPSAPI(LPCTSTR lpszModuleName,DWORD *dwProductVersionMS = NULL, DWORD *dwProductVersionLS = NULL)
1874{
1875 static HMODULE hModPSAPI = NULL;
1876 static PFNENUMPROCESSES pfnEnumProcesses = 0;
1877 static PFNENUMPROCESSMODULES pfnEnumProcessModules = 0;
1878 static PFNGETMODULEFILENAMEEXA pfnGetModuleFileNameExA = 0;
1879
1880 // Hook up to the functions in PSAPI.DLL dynamically.
1881 if ( NULL == hModPSAPI )
1882 {
1883 hModPSAPI = ::LoadLibraryW(L"PSAPI.DLL");
1884
1885 if (NULL == hModPSAPI)
1886 return _FP_SYSTEM_DLL_NOT_FOUND;
1887 }
1888
1889 pfnEnumProcesses = (PFNENUMPROCESSES) ::GetProcAddress(hModPSAPI,"EnumProcesses");
1890
1891 pfnEnumProcessModules = (PFNENUMPROCESSMODULES) ::GetProcAddress(hModPSAPI, "EnumProcessModules");
1892
1893 pfnGetModuleFileNameExA = (PFNGETMODULEFILENAMEEXA) ::GetProcAddress(hModPSAPI, "GetModuleFileNameExA");
1894
1895 if ( (NULL == pfnEnumProcesses) ||
1896 (NULL == pfnEnumProcessModules) ||
1897 (NULL == pfnGetModuleFileNameExA) )
1898 return _FP_SYSTEM_DLL_NOT_FOUND;
1899
1900 // Successfully hooked upto PSAPI.DLL functions
1901 DWORD pidArray[1024];
1902 DWORD cbNeeded;
1903 DWORD nProcesses;
1904
1905 // EnumProcesses returns an array of process IDs
1906 if ( NULL == pfnEnumProcesses(pidArray, sizeof(pidArray), &cbNeeded) )
1907 return _FP_GENERIC_ERROR;
1908
1909 nProcesses = cbNeeded / sizeof(DWORD); // Determine number of processes
1910
1911 // Iterate through each process in the array
1912 for ( unsigned i = 0; i < nProcesses; i++ )
1913 {
1914 HMODULE hModuleArray[1024];
1915 HANDLE hProcess;
1916 DWORD pid = pidArray[i];
1917 DWORD nModules;
1918
1919 // Use the process ID to open up a handle to the process
1920 hProcess = ::OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
1921 if ( NULL == hProcess )
1922 continue;
1923
1924 // EnumProcessModules returns an array of HMODULEs for the process
1925 if ( NULL == pfnEnumProcessModules(hProcess, hModuleArray, sizeof(hModuleArray), &cbNeeded ) )
1926 {
1927 ::CloseHandle( hProcess );
1928 continue;
1929 }
1930
1931 // Calculate number of modules in the process
1932 nModules = cbNeeded / sizeof(hModuleArray[0]);
1933
1934 // Iterate through each of the process's modules
1935 for ( unsigned j=0; j < nModules; j++ )
1936 {
1937 HMODULE hModule = hModuleArray[j];
1938 TCHAR szModuleName[MAX_PATH];
1939
1940 pfnGetModuleFileNameExA(hProcess, hModule, szModuleName, sizeof(szModuleName) );
1941
1942 if ( 0 == j ) // The first module is always the EXE
1943 {
1944
1945 if (_tcsstr(_tcslwr(szModuleName),_tcslwr((TCHAR *)lpszModuleName)))
1946 {
1947 DWORD dwHandle = 0;
1948 DWORD dwSize = ::GetFileVersionInfoSize((TCHAR *)(const TCHAR *) szModuleName, &dwHandle);
1949 if (NULL == dwSize)
1950 return _FP_GENERIC_ERROR; // EXE doesn't have VERSIONINFO data?
1951
1952
1953 // Allocate data buffers of the proper sizes.
1954 LPSTR pVerData = DEBUG_NEW_NOTHROW TCHAR[dwSize];
1955 if (NULL == pVerData)
1956 return _FP_GENERIC_ERROR;
1957
1958 // Fetching the actual VERSIONINFO data.
1959 if (! ::GetFileVersionInfo((TCHAR *)(const TCHAR *) szModuleName, dwHandle, dwSize, pVerData))
1960
1961 {
1962 ASSERT(0); // missing VERSIONINFO data?
1963 delete [] pVerData;
1964 return _FP_GENERIC_ERROR;
1965 }
1966
1967 LPSTR pVsFixedFileInfoData = NULL;
1968 UINT bufsize = 0;
1969 TCHAR sztmpStr[5];
1970 _tcscpy(sztmpStr, "\\");
1971 if (::VerQueryValueA(pVerData, sztmpStr, (void **) &pVsFixedFileInfoData, &bufsize))
1972 {
1973 if (pVsFixedFileInfoData && bufsize)
1974 {
1975 VS_FIXEDFILEINFO* p_info = (VS_FIXEDFILEINFO *) pVsFixedFileInfoData;
1976 *dwProductVersionMS = p_info->dwProductVersionMS;
1977 *dwProductVersionLS = p_info->dwProductVersionLS;
1978 }
1979 }
1980
1981 delete [] pVerData;
1982 return _FP_SUCCESS;
1983 }
1984
1985 }
1986 }
1987
1988 ::CloseHandle( hProcess ); // Closing the process handle
1989 }
1990
1991 return _FP_QUERIED_MODULE_NOT_FOUND;
1992}
1993
1994//BOOL IsCurrentlyExecuting(LPCTSTR lpszModuleName)
1995BOOL IsCurrentlyExecuting(LPCTSTR lpszModuleName, DWORD *dwProductVersionMS, DWORD *dwProductVersionLS)
1996{
1997 DWORD dwReturn;
1998
1999 dwReturn = FindifExecutingUsingToolHelp32(lpszModuleName,dwProductVersionMS, dwProductVersionLS);
2000
2001 if ( _FP_SYSTEM_DLL_NOT_FOUND == dwReturn)
2002 {
2003 // ToolHelp32 was perhaps not present.Let's try PSAPI.DLL
2004 dwReturn = FindifExecutingUsingPSAPI(lpszModuleName,dwProductVersionMS, dwProductVersionLS);
2005 }
2006
2007 return ((dwReturn == _FP_SUCCESS) ? TRUE : FALSE);
2008}
2009
2010
2011// Function to check if netscape is executing & if so is the version greater than or equal to 4.05
2012// Actually we are not really interested in knowing if it is executing or not. What matters FOR NOW is the version (if at all executing)
2013BOOL IsNetscapeAbove405Executing()
2014{
2015 DWORD dwProductVersionMS = 0, dwProductVersionLS = 0;
2016
2017 // See if Netscape is Executing
2018 if (_FP_SUCCESS == IsCurrentlyExecuting("netscape.exe", &dwProductVersionMS, &dwProductVersionLS))
2019 {
2020 // Check if the version that is executing is 4.05 or above
2021 if (dwProductVersionMS >= _NETSCAPE_405_PRODUCT_VERSION_MS_)
2022 {
2023 return TRUE;
2024 }
2025 }
2026 return FALSE;
2027}
2028
2029char* StripAddressMT(char* line)
2030{
2031 if (!line)
2032 return (NULL);
2033
2034 // Remove parenthesized comments
2035 char *p = line;
2036 char *t = line;
2037 int InParen = 0;
2038 int InQuote = 0;
2039 char Last = 0;
2040 for (; *p; p++)
2041 {
2042 if (Last == '\\')
2043 {
2044 if (*p == '\\')
2045 Last = 0;
2046 else
2047 Last = *p;
2048 }
2049 else
2050 {
2051 Last = *p;
2052 if (!InQuote && *p == '(')
2053 {
2054 InParen++;
2055 continue;
2056 }
2057 else if (InParen && *p == ')')
2058 {
2059 InParen--;
2060 continue;
2061 }
2062 else if (*p == '"')
2063 InQuote = !InQuote;
2064 }
2065 if (!InParen)
2066 *t++ = *p;
2067 }
2068 *t = 0;
2069
2070 char* BegAngle = (char *) strchr(line, '<');
2071 char* EndAngle = (BegAngle != NULL) ? strchr(BegAngle + 1, '>') : NULL;
2072 if (BegAngle && EndAngle && BegAngle < EndAngle)
2073 {
2074 *EndAngle = 0;
2075 strcpy(line, BegAngle + 1);
2076 }
2077
2078 return (::TrimWhitespaceMT(line));
2079}
2080
2081// This function parses the RFC822 date string into Time Zone (in Minutes) &
2082// the actual time (in seconds)
2083// Note : This has been moved here from Summary.cpp
2084void FormatDateMT(const char* GMTOffset, long &lSeconds, int &nTimeZoneMinutes)
2085{
2086 char GMTbuf[128];
2087
2088 if (GMTOffset)
2089 {
2090 strncpy(GMTbuf, GMTOffset, sizeof(GMTbuf) - 1);
2091 GMTbuf[sizeof(GMTbuf) - 1] = 0;
2092
2093 // StripAddress() will strip out () comments
2094 StripAddressMT(GMTbuf);
2095
2096 GMTOffset = strrchr(GMTbuf, ' ');
2097 if (GMTOffset && GMTOffset[1] != '-' && GMTOffset[1] != '+' &&
2098 !isalpha((int)(unsigned char)(GMTOffset[1])))
2099 {
2100 GMTOffset = NULL;
2101 }
2102 }
2103
2104 // Store seconds as GMT time so sorts by date occur correctly
2105 if (GMTOffset)
2106 {
2107 GMTOffset++;
2108 if (!isdigit((int)(unsigned char)*GMTOffset) && *GMTOffset != '-' && *GMTOffset != '+')
2109 {
2110 // Do we have a timezone abbreviation for it
2111 int len = strlen(GMTOffset);
2112 CString ZoneInfo;
2113 for (UINT i = IDS_TZ_FIRST; TRUE; i++)
2114 {
2115 if (!ZoneInfo.LoadString(i) || ZoneInfo.IsEmpty())
2116 break;
2117 if (!strnicmp(ZoneInfo, GMTOffset, len))
2118 {
2119 strcpy((char *)GMTOffset,(const char*)ZoneInfo);
2120 GMTOffset += len;
2121 while(!isdigit((int)(unsigned char)*GMTOffset) && *GMTOffset != '-' && *GMTOffset != '+')
2122 GMTOffset++;
2123 break;
2124 }
2125 }
2126 }
2127 int num = atoi(GMTOffset);
2128
2129 // Check for bogus timezones. Ignore if bad.
2130 if (num >= -2400 && num <= 2400)
2131 {
2132 nTimeZoneMinutes = num / 100 * 60;
2133
2134 // Deal with minutes if there are some
2135 num %= 100;
2136 if (num)
2137 nTimeZoneMinutes += num;
2138
2139 lSeconds -= nTimeZoneMinutes * 60;
2140 }
2141 }
2142}
2143
2144// ComposeDate
2145// Get the info together to compose an RFC-822 happy date stamp
2146//
2147int ComposeDateMT(char* DateBuf, unsigned long GMTTime, int TimeZoneMinutes, BOOL bEudorasSMTPFormat /* = TRUE */ )
2148{
2149 if (DateBuf)
2150 {
2151 *DateBuf = 0;
2152
2153 // Date must be in the format of
2154 // "Date: <weekday> , <date> <month> <year> HH:MM:SS <gmt offset>"
2155 // where <gmt offset> = [+|-]HHMM for current time zone
2156
2157 GMTTime += TimeZoneMinutes * 60;
2158
2159 CTime Time = GMTTime;
2160 if (Time.GetTime() < 0)
2161 Time = 0;
2162
2163 // We need to use the string resources that have English month and day of week abbreviations
2164 // because Date: headers should always be in English. All other routines wind up using
2165 // strftime(), which localizes month and day of week names in to the current language.
2166 const char* ThisWeekday = &szWeekdays_M[(Time.GetDayOfWeek() - 1) * 3];
2167 const char* ThisMonth = &szMonths_M[(Time.GetMonth() - 1) * 3];
2168
2169 // In Eudora, this format string was stored as a resource string (IDS_SMTP_DATE_FORMAT). Since I see no
2170 // reason that it need be such, I have hard-wired it here for Hermes. Pete Maclean, 8-Nov-2018.
2171 static const char szSMTPDateFormat[] = "Date: %0.3s, %02d %0.3s %4d %02d:%02d:%02d";
2172 static const char szDateFormat[] = "%0.3s, %02d %0.3s %4d %02d:%02d:%02d";
2173
2174 const char *pszFormat = (bEudorasSMTPFormat) ? szSMTPDateFormat : szDateFormat;
2175
2176 sprintf(DateBuf, pszFormat,
2177 ThisWeekday,
2178 Time.GetDay(),
2179 ThisMonth,
2180 Time.GetYear(),
2181 Time.GetHour(),
2182 Time.GetMinute(),
2183 Time.GetSecond());
2184 TimeDateStringFormatMT(DateBuf + strlen(DateBuf), 0, TimeZoneMinutes, " %4");
2185
2186 return TRUE;
2187 }
2188 else
2189 return FALSE;
2190}
2191
2192//Date: %0.3s, %02d %0.3s %4d %02d:%02d:%02d
2193
2194BOOL Weekday(const char* Line)
2195{
2196 ASSERT(Line);
2197
2198 for (LPCTSTR w = szWeekdays_M; *w; w += 3)
2199 {
2200 if (strnicmp(Line, w, 3) == 0)
2201 return TRUE;
2202 }
2203
2204 return FALSE;
2205}
2206
2207
2208// GetTime
2209//
2210// If FromLine is TRUE and Line is not a valid Unix-style From line, then
2211// return 0L. Otherwise, return the time given in the line, if parsable.
2212// Here's a sample From line:
2213// From beckley@qualcomm.com Thu Oct 15 16:15:08 1992
2214//
2215long GetTimeMT(const char* Line, BOOL FromLine)
2216{
2217 // alternate from line
2218 //
2219 // Note that we're careful to specify the maximum width that we want for
2220 // the timezone string portion - using sscanf without this is begging for
2221 // a buffer overflow crash, which is exactly what we were seeing for certain
2222 // corrupt mailboxes before this was added.
2223 static const char* AltFromScan1 = "%3s %d %d:%d 7%s %d";
2224 static const char* AltFromScan2 = "%3s %d %d:%d:%d 7%s %d";
2225 char timeZone[8];
2226
2227 struct tm time;
2228 char month[5];
2229 int mon;
2230 int fraction;
2231
2232 if (!Line) return (0L);
2233
2234 time.tm_year = time.tm_hour = time.tm_min = time.tm_sec = 0;
2235 if (FromLine)
2236 {
2237 // Check for "From "
2238 if (Line[0] != 'F' || Line[1] != 'r' || Line[2] != 'o' ||
2239 Line[3] != 'm' || Line[4] != ' ')
2240 {
2241 return (0L);
2242 }
2243 Line += 5;
2244
2245 // Skip address
2246 while (*Line && (*Line == ' ' || *Line == '\t'))
2247 Line++;
2248 while (*Line && *Line != ' ' && *Line != '\t')
2249 Line++;
2250 while (*Line && (*Line == ' ' || *Line == '\t'))
2251 Line++;
2252
2253 // Verify weekday
2254 if (!Weekday(Line))
2255 return (0L);
2256
2257 // Skip the weekday
2258 while (*Line && *Line != ' ' && *Line != '\t')
2259 Line++;
2260 while (*Line && (*Line == ' ' || *Line == '\t'))
2261 Line++;
2262
2263 if (!*Line)
2264 return (0L);
2265
2266 // Read in rest of date
2267 if (sscanf(Line, szScanDate1_M, month, &time.tm_mday,
2268 &time.tm_hour, &time.tm_min, &time.tm_sec, &time.tm_year) != 6)
2269 {
2270 if (sscanf(Line, AltFromScan1, month, &time.tm_mday, &time.tm_hour,
2271 &time.tm_min, timeZone, &time.tm_year) != 6)
2272 {
2273 sscanf(Line, AltFromScan2, month, &time.tm_mday, &time.tm_hour,
2274 &time.tm_min, &time.tm_sec, timeZone, &time.tm_year);
2275 }
2276 }
2277 }
2278 else
2279 {
2280 while (*Line && !isdigit((int)(unsigned char)*Line))
2281 Line++;
2282 if (!*Line)
2283 return (0L);
2284 if ((sscanf(Line, szScanDate2_M, &time.tm_mday, month,
2285 &time.tm_year, &time.tm_hour, &time.tm_min, &time.tm_sec) < 5) &&
2286 (sscanf(Line, szScanDate3_M, &time.tm_mday, month,
2287 &time.tm_year, &time.tm_hour, &time.tm_min, &time.tm_sec, &fraction) < 7))
2288 {
2289 return (0L);
2290 }
2291 }
2292
2293 // Find corresponding month number
2294 const char *MonthIndex = szMonths_M;
2295 mon = -1;
2296 for (int i = 0; *MonthIndex != 0; MonthIndex += 3, i++)
2297 {
2298 if (strnicmp(month, MonthIndex, 3) == 0)
2299 {
2300 mon = i;
2301 break;
2302 }
2303 }
2304 if (mon < 0)
2305 return (0L);
2306 time.tm_mon = mon;
2307
2308 // Get year as number of years past 1900
2309 if (time.tm_year < 70)
2310 time.tm_year += 100;
2311 else if (time.tm_year > 1900)
2312 {
2313 time.tm_year -= 1900;
2314 if (time.tm_year < 70)
2315 time.tm_year = 70;
2316 }
2317
2318 time.tm_isdst = -1;
2319 time_t seconds = mktime(&time);
2320
2321 // Ok, we've got this far. This is probably a "From " line of some
2322 // variety, but the rest of it may not match up. So if we get a bogus
2323 // time, still consider it a "From " line, but we don't know exactly what
2324 // date/time it represents, so just return 1.
2325
2326 return (seconds < 0L? (FromLine? 1L : 0L) : seconds);
2327}
2328
2329
2330// Wrapper for functions needed to determine Disk Spun state
2331// In Win2000,Win98 & above GetDeviceState is implemented ( & works)
2332// In Win95 & above, GetSystemPowerStatus works (altho' the kernel has this in WinNT 4.0 too)
2333
2334/*Excerpt from : http://msdn.microsoft.com/library/backgrnd/html/onnowapp.htm
2335Power Status Functions
2336Applications can get system power status to tune behavior. The best example of this is the possible work that the applications can do
2337to preserve battery life on mobile PCs. When a PC is running on batteries, the operating system will attempt to put the CPU into a low
2338power state and to spin down the hard disk whenever idle, because the processor and hard disk are two of the highest power devices
2339in mobile PCs. Applications that do not pay attention to the power state of the system can cause the hard disk to spin up frequently,
2340thus losing all savings gained from turning off the drive.
2341
2342For a mobile PC, an application can track system status information and stop nonessential background tasks such as background
2343pagination. To prevent the hard disk from spinning up frequently, the application can postpone low-priority tasks while the PC is
2344running on battery power.
2345
2346Applications can also get the power state of a device to optimize power consumption.*/
2347
2348// This is the filename of a temporary file which is required, so that we have a valid handle to the file when calling GetDevicePowerState.
2349const TCHAR gszTempFileName[] = "DoNotDel.tmp";
2350
2351class CShouldFileIOBeDone
2352{
2353private:
2354 HANDLE m_hFile; // Handle of File
2355
2356 // _MAX_PATH is defined as 255 & hence using 2048 instead
2357
2358 TCHAR szFileName[_MAX_FNAME + _MAX_EXT + sizeof(TCHAR)]; // Name of the File
2359 TCHAR szDirectory [2048 + sizeof(TCHAR)];
2360
2361 typedef BOOL (WINAPI * PFNGETDEVICEPOWERSTATE)(HANDLE hFile, BOOL *pState);
2362
2363 // Function pointer for GetDevicePowerState(HANDLE, BOOL *)
2364 PFNGETDEVICEPOWERSTATE m_pfnGetDevicePowerState;
2365
2366public :
2367 CShouldFileIOBeDone();
2368 ~CShouldFileIOBeDone()
2369 {
2370 if (m_hFile)
2371 {
2372 ::CloseHandle(m_hFile);
2373 remove(szFileName); // delete the file
2374 m_hFile = NULL;
2375 }
2376 }
2377
2378 void SetDirectory(LPCTSTR szDir);
2379
2380 BOOL IsDiskSpun(BOOL &bSpunState);
2381 BOOL IsSystemOnBattery(BOOL &bOnBattery);
2382 BOOL ShouldFileIOBeDone(BOOL &bShouldFileIOBeDone);
2383};
2384
2385// Golbal
2386static CShouldFileIOBeDone gShouldFileIOBeDone;
2387
2388
2389CShouldFileIOBeDone::CShouldFileIOBeDone()
2390{
2391 m_hFile = NULL;
2392 *szDirectory = '\0';
2393}
2394
2395void CShouldFileIOBeDone::SetDirectory(LPCTSTR szDir)
2396{
2397 TCHAR szBuffer[4096];
2398 // Set the directory
2399 _tcscpy(szDirectory, szDir);
2400
2401 static HMODULE hModKERNEL32 = NULL;
2402 m_pfnGetDevicePowerState = NULL;
2403
2404 //try - compiler warned that try wasn't necessary
2405 //{
2406 if ( NULL == hModKERNEL32 )
2407 {
2408 hModKERNEL32 = GetModuleHandleW(L"KERNEL32.DLL");
2409
2410 if (!m_pfnGetDevicePowerState)
2411 m_pfnGetDevicePowerState = (PFNGETDEVICEPOWERSTATE) GetProcAddress( hModKERNEL32, "GetDevicePowerState" );
2412
2413 if(m_pfnGetDevicePowerState)
2414 {
2415 _tcscpy(szFileName,gszTempFileName);
2416 // Create/Open the file, which we will need to pass to GetDevicePowerState whenever requested
2417 if (0 == strlen(szDirectory))
2418 {
2419 // If there isn't a data directory specified by Eudora, then take the current directory
2420 GetCurrentDirectory(sizeof(szDirectory),szDirectory);
2421 strcat(szDirectory,"\\");
2422 }
2423
2424 strcpy(szBuffer,szDirectory);
2425 // Eudora's data directory already consists of a trailing "\\"
2426 strcat(szBuffer,szFileName);
2427 m_hFile = ::CreateFileA(szBuffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS,
2428 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, NULL);
2429 }
2430 }
2431
2432 //}
2433 //catch(...)
2434 //{
2435 // m_pfnGetDevicePowerState = NULL;
2436 //}
2437}
2438
2439BOOL CShouldFileIOBeDone::IsDiskSpun(BOOL &bSpunState)
2440{
2441 static HMODULE hModKERNEL32 = 0;
2442 static PFNGETDEVICEPOWERSTATE pfnGetDevicePowerState = NULL;
2443
2444 try
2445 {
2446 if (!m_pfnGetDevicePowerState) // Not supported by kernel
2447 return FALSE;
2448
2449 if (m_pfnGetDevicePowerState&& m_hFile)
2450 m_pfnGetDevicePowerState(m_hFile,&bSpunState); // Call the GetDevicePowerState
2451 else
2452 return FALSE; // Ideally this should not happen
2453
2454 return TRUE;
2455 }
2456 catch (CMemoryException * /* pMemoryException */)
2457 {
2458 // Catastrophic memory exception - rethrow
2459 ASSERT( !"Rethrowing CMemoryException in CShouldFileIOBeDone::IsDiskSpun" );
2460 throw;
2461 }
2462 catch (CException * pException)
2463 {
2464 // Other MFC exception
2465 pException->Delete();
2466 ASSERT( !"Caught CException (not CMemoryException) in CShouldFileIOBeDone::IsDiskSpun" );
2467 }
2468 catch (std::bad_alloc & /* exception */)
2469 {
2470 // Catastrophic memory exception - rethrow
2471 ASSERT( !"Rethrowing std::bad_alloc in CShouldFileIOBeDone::IsDiskSpun" );
2472 throw;
2473 }
2474 catch (std::exception & /* exception */)
2475 {
2476 ASSERT( !"Caught std::exception (not std::bad_alloc) in CShouldFileIOBeDone::IsDiskSpun" );
2477 }
2478
2479 // Something went wrong - return FALSE
2480 return FALSE;
2481}
2482
2483BOOL CShouldFileIOBeDone::IsSystemOnBattery(BOOL &bOnBattery)
2484{
2485 SYSTEM_POWER_STATUS SystemPower;
2486 if (::GetSystemPowerStatus(&SystemPower))
2487 {
2488 if (SystemPower.ACLineStatus == 0) // offline
2489 bOnBattery = TRUE;
2490 else if (SystemPower.ACLineStatus == 1) // online
2491 bOnBattery = FALSE;
2492 else
2493 return FALSE; // Unknown status
2494 }
2495 else
2496 return FALSE; // Don't know ..probably because it's not supported by the kernel
2497
2498 return TRUE;
2499}
2500
2501BOOL CShouldFileIOBeDone::ShouldFileIOBeDone(BOOL &bShouldFileIOBeDone)
2502{
2503 BOOL bSpunState = FALSE, bOnBattery = FALSE;
2504
2505 if (IsDiskSpun(bSpunState))
2506 bShouldFileIOBeDone = bSpunState;
2507 else if (IsSystemOnBattery(bOnBattery))
2508 bShouldFileIOBeDone = !bOnBattery;
2509 else
2510 return FALSE;
2511
2512 return TRUE;
2513}
2514
2515// Exported
2516// This function first determines the disk spun state if it can ('coz this feature is supported only in Win2000, Win98 & above
2517// If the disk is not spun the function sets bSpunState to FALSE.
2518//
2519// The function returns FALSE if it cannot determine the state either thru' GetDevicePowerState.
2520// Check the function's return value. If it's TRUE, then only bSpunState will be set correctly.
2521// Whenever the function returns FALSE, ignore the bSpunState value.
2522
2523BOOL IsDiskSpun(BOOL &bSpunState)
2524{
2525 return gShouldFileIOBeDone.IsDiskSpun(bSpunState);
2526}
2527
2528// Exported
2529// The function determines if the system is using a battery(DC) or AC (this is avalaible in Win95 & above)
2530// If the system is using battery, the function sets bOnBattery to FALSE.
2531//
2532// The function returns FALSE if it cannot determine the state either thru' GetSystemPowerStatus.
2533// Check the function's return value. If it's TRUE, then only bOnBattery will be set correctly.
2534// Whenever the function returns FALSE, ignore the bOnBattery value.
2535
2536BOOL IsSystemOnBattery(BOOL &bOnBattery)
2537{
2538 return gShouldFileIOBeDone.IsSystemOnBattery(bOnBattery);
2539}
2540
2541// Exported
2542// This function first determines the disk spun state if it can ('coz this feature is supported only in Win2000, Win98 & above
2543// If not, then it tries to see if the system is using a battery(DC) or AC (this is avalaible in Win95 & above)
2544// If the disk is not spun or if the system is using battery, the function sets bShouldFileIOBeDone to FALSE.
2545//
2546// The function returns FALSE if it cannot determine the state either thru' GetDevicePowerState or GetSystemPowerStatus.
2547// Check the function's return value. If it's TRUE, then only bShouldFileIOBeDone will be set correctly.
2548// Whenever the function returns FALSE, ignore the bShouldFileIOBeDone value.
2549
2550BOOL ShouldFileIOBeDone(BOOL &bShouldFileIOBeDone)
2551{
2552 return gShouldFileIOBeDone.ShouldFileIOBeDone(bShouldFileIOBeDone);
2553}
2554
2555// Exported
2556// Set a temp directory, where the file will be created, which will then be opened & used for the above IsDiskSpun API.
2557void SetTempFileDirectory(LPCTSTR lpszDirectory)
2558{
2559 // Update this function if lpszDirectory could be used anywhere else too
2560 gShouldFileIOBeDone.SetDirectory(lpszDirectory);
2561}
2562