2014-06-25 07:25:55 +00:00
/ *
This file is part of Telegram Desktop ,
2018-01-03 10:23:14 +00:00
the official desktop application for the Telegram messaging service .
2014-06-25 07:25:55 +00:00
2018-01-03 10:23:14 +00:00
For license and copyright information please follow this link :
https : // github . com / telegramdesktop / tdesktop / blob / master / LEGAL
2014-06-25 07:25:55 +00:00
* /
# import < Cocoa / Cocoa . h >
2019-01-29 10:09:37 +00:00
# include < sys / xattr . h >
2014-06-25 07:25:55 +00:00
NSString * appName = @ "Telegram.app" ;
NSString * appDir = nil ;
NSString * workDir = nil ;
# ifdef _DEBUG
BOOL _debug = YES ;
# else
BOOL _debug = NO ;
# endif
NSFileHandle * _logFile = nil ;
void openLog ( ) {
if ( ! _debug || _logFile ) return ;
NSString * logDir = [ workDir stringByAppendingString : @ "DebugLogs" ] ;
if ( ! [ [ NSFileManager defaultManager ] createDirectoryAtPath : logDir withIntermediateDirectories : YES attributes : nil error : nil ] ) {
return ;
}
2015-06-04 11:59:30 +00:00
NSDateFormatter * fmt = [ [ NSDateFormatter alloc ] initWithDateFormat : @ "DebugLogs/%Y%m%d_%H%M%S_upd.txt" allowNaturalLanguage : NO ] ;
2014-06-25 07:25:55 +00:00
NSString * logPath = [ workDir stringByAppendingString : [ fmt stringFromDate : [ NSDate date ] ] ] ;
[ [ NSFileManager defaultManager ] createFileAtPath : logPath contents : nil attributes : nil ] ;
_logFile = [ NSFileHandle fileHandleForWritingAtPath : logPath ] ;
}
void closeLog ( ) {
if ( ! _logFile ) return ;
[ _logFile closeFile ] ;
}
void writeLog ( NSString * msg ) {
if ( ! _logFile ) return ;
[ _logFile writeData : [ [ msg stringByAppendingString : @ "\n" ] dataUsingEncoding : NSUTF8StringEncoding ] ] ;
[ _logFile synchronizeFile ] ;
}
2019-01-29 10:09:37 +00:00
void RemoveQuarantineAttribute ( NSString * path ) {
const char * kQuarantineAttribute = "com.apple.quarantine" ;
writeLog ( [ @ "Removing quarantine: " stringByAppendingString : path ] ) ;
removexattr ( [ path fileSystemRepresentation ] , kQuarantineAttribute , 0 ) ;
}
void RemoveQuarantineFromBundle ( NSString * path ) {
2020-01-02 11:25:52 +00:00
RemoveQuarantineAttribute ( path ) ;
RemoveQuarantineAttribute ( [ path stringByAppendingString : @ "/Contents/MacOS/Telegram" ] ) ;
RemoveQuarantineAttribute ( [ path stringByAppendingString : @ "/Contents/Helpers/crashpad_handler" ] ) ;
RemoveQuarantineAttribute ( [ path stringByAppendingString : @ "/Contents/Frameworks/Updater" ] ) ;
2019-01-29 10:09:37 +00:00
}
2014-06-25 07:25:55 +00:00
void delFolder ( ) {
2015-06-04 10:52:16 +00:00
writeLog ( [ @ "Fully clearing old path: " stringByAppendingString : [ workDir stringByAppendingString : @ "tupdates/ready" ] ] ) ;
if ( ! [ [ NSFileManager defaultManager ] removeItemAtPath : [ workDir stringByAppendingString : @ "tupdates/ready" ] error : nil ] ) {
writeLog ( @ "Failed to clear old path! :( New path was used?.." ) ;
}
writeLog ( [ @ "Fully clearing new path: " stringByAppendingString : [ workDir stringByAppendingString : @ "tupdates/temp" ] ] ) ;
if ( ! [ [ NSFileManager defaultManager ] removeItemAtPath : [ workDir stringByAppendingString : @ "tupdates/temp" ] error : nil ] ) {
writeLog ( @ "Error: failed to clear new path! :(" ) ;
}
2014-06-25 07:25:55 +00:00
rmdir ( [ [ workDir stringByAppendingString : @ "tupdates" ] fileSystemRepresentation ] ) ;
}
int main ( int argc , const char * argv [ ] ) {
NSString * path = [ [ NSBundle mainBundle ] bundlePath ] ;
if ( ! path ) {
return -1 ;
}
2014-11-27 18:20:48 +00:00
NSRange range = [ path rangeOfString : @ ".app/" options : NSBackwardsSearch ] ;
2014-06-25 07:25:55 +00:00
if ( range . location = = NSNotFound ) {
return -1 ;
}
2014-11-27 18:20:48 +00:00
path = [ path substringToIndex : range . location > 0 ? range . location : 0 ] ;
range = [ path rangeOfString : @ "/" options : NSBackwardsSearch ] ;
NSString * appRealName = ( range . location = = NSNotFound ) ? path : [ path substringFromIndex : range . location + 1 ] ;
appRealName = [ [ NSArray arrayWithObjects : appRealName , @ ".app" , nil ] componentsJoinedByString : @ "" ] ;
appDir = ( range . location = = NSNotFound ) ? @ "" : [ path substringToIndex : range . location + 1 ] ;
NSString * appDirFull = [ appDir stringByAppendingString : appRealName ] ;
2014-06-25 07:25:55 +00:00
openLog ( ) ;
pid_t procId = 0 ;
2020-05-20 00:10:21 +00:00
BOOL update = YES , toSettings = NO , autoStart = NO , startInTray = NO , testMode = NO , freeType = NO , externalUpdater = NO ;
2017-12-12 07:52:53 +00:00
BOOL customWorkingDir = NO ;
2014-06-25 07:25:55 +00:00
NSString * key = nil ;
for ( int i = 0 ; i < argc ; + + i ) {
if ( [ @ "-workpath" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
if ( + + i < argc ) {
workDir = [ NSString stringWithUTF8String : argv [ i ] ] ;
}
} else if ( [ @ "-procid" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
if ( + + i < argc ) {
NSNumberFormatter * formatter = [ [ NSNumberFormatter alloc ] init ] ;
[ formatter setNumberStyle : NSNumberFormatterDecimalStyle ] ;
procId = [ [ formatter numberFromString : [ NSString stringWithUTF8String : argv [ i ] ] ] intValue ] ;
}
} else if ( [ @ "-noupdate" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
update = NO ;
} else if ( [ @ "-tosettings" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
toSettings = YES ;
} else if ( [ @ "-autostart" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
autoStart = YES ;
} else if ( [ @ "-debug" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
_debug = YES ;
2015-01-27 16:58:58 +00:00
} else if ( [ @ "-startintray" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
startInTray = YES ;
2015-04-02 10:33:19 +00:00
} else if ( [ @ "-testmode" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
testMode = YES ;
2020-05-20 00:10:21 +00:00
} else if ( [ @ "-freetype" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
freeType = YES ;
2018-07-11 22:14:44 +00:00
} else if ( [ @ "-externalupdater" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
externalUpdater = YES ;
2017-12-12 07:52:53 +00:00
} else if ( [ @ "-workdir_custom" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
customWorkingDir = YES ;
2014-06-25 07:25:55 +00:00
} else if ( [ @ "-key" isEqualToString : [ NSString stringWithUTF8String : argv [ i ] ] ] ) {
if ( + + i < argc ) key = [ NSString stringWithUTF8String : argv [ i ] ] ;
}
}
2017-12-12 07:52:53 +00:00
if ( ! workDir ) {
workDir = appDir ;
customWorkingDir = NO ;
}
2014-06-25 07:25:55 +00:00
openLog ( ) ;
NSMutableArray * argsArr = [ [ NSMutableArray alloc ] initWithCapacity : argc ] ;
for ( int i = 0 ; i < argc ; + + i ) {
[ argsArr addObject : [ NSString stringWithUTF8String : argv [ i ] ] ] ;
}
writeLog ( [ [ NSArray arrayWithObjects : @ "Arguments: '" , [ argsArr componentsJoinedByString : @ "' '" ] , @ "'.." , nil ] componentsJoinedByString : @ "" ] ) ;
if ( key ) writeLog ( [ @ "Key: " stringByAppendingString : key ] ) ;
if ( toSettings ) writeLog ( @ "To Settings!" ) ;
if ( procId ) {
NSRunningApplication * app = [ NSRunningApplication runningApplicationWithProcessIdentifier : procId ] ;
for ( int i = 0 ; i < 5 && app ! = nil && ! [ app isTerminated ] ; + + i ) {
usleep ( 200000 ) ;
app = [ NSRunningApplication runningApplicationWithProcessIdentifier : procId ] ;
}
if ( app ) [ app forceTerminate ] ;
app = [ NSRunningApplication runningApplicationWithProcessIdentifier : procId ] ;
for ( int i = 0 ; i < 5 && app ! = nil && ! [ app isTerminated ] ; + + i ) {
usleep ( 200000 ) ;
app = [ NSRunningApplication runningApplicationWithProcessIdentifier : procId ] ;
}
}
if ( update ) {
NSFileManager * fileManager = [ NSFileManager defaultManager ] ;
2015-06-03 18:13:01 +00:00
NSString * readyFilePath = [ workDir stringByAppendingString : @ "tupdates/temp/ready" ] ;
NSString * srcDir = [ workDir stringByAppendingString : @ "tupdates/temp/" ] , * srcEnum = [ workDir stringByAppendingString : @ "tupdates/temp" ] ;
2015-06-04 10:52:16 +00:00
if ( [ fileManager fileExistsAtPath : readyFilePath ] ) {
writeLog ( [ @ "Ready file found! Using new path: " stringByAppendingString : srcEnum ] ) ;
} else {
2015-06-03 18:13:01 +00:00
srcDir = [ workDir stringByAppendingString : @ "tupdates/ready/" ] ; // old
srcEnum = [ workDir stringByAppendingString : @ "tupdates/ready" ] ;
2015-06-04 10:52:16 +00:00
writeLog ( [ @ "Ready file not found! Using old path: " stringByAppendingString : srcEnum ] ) ;
2015-06-03 18:13:01 +00:00
}
writeLog ( [ @ "Starting update files iteration, path: " stringByAppendingString : srcEnum ] ) ;
2016-09-12 15:48:01 +00:00
// Take the Updater ( this currently running binary ) from the place where it was placed by Telegram
// and copy it to the folder with the new version of the app ( ready ) ,
// so it won ' t be deleted when we will clear the "Telegram.app/Contents" folder .
NSString * oldVersionUpdaterPath = [ appDirFull stringByAppendingString : @ "/Contents/Frameworks/Updater" ] ;
NSString * newVersionUpdaterPath = [ srcEnum stringByAppendingString : [ [ NSArray arrayWithObjects : @ "/" , appName , @ "/Contents/Frameworks/Updater" , nil ] componentsJoinedByString : @ "" ] ] ;
writeLog ( [ [ NSArray arrayWithObjects : @ "Copying Updater from old path " , oldVersionUpdaterPath , @ " to new path " , newVersionUpdaterPath , nil ] componentsJoinedByString : @ "" ] ) ;
if ( ! [ fileManager fileExistsAtPath : newVersionUpdaterPath ] ) {
if ( ! [ fileManager copyItemAtPath : oldVersionUpdaterPath toPath : newVersionUpdaterPath error : nil ] ) {
writeLog ( [ [ NSArray arrayWithObjects : @ "Failed to copy file from " , oldVersionUpdaterPath , @ " to " , newVersionUpdaterPath , nil ] componentsJoinedByString : @ "" ] ) ;
delFolder ( ) ;
return -1 ;
}
}
2016-09-02 05:27:54 +00:00
NSString * contentsPath = [ appDirFull stringByAppendingString : @ "/Contents" ] ;
writeLog ( [ [ NSArray arrayWithObjects : @ "Clearing dir " , contentsPath , nil ] componentsJoinedByString : @ "" ] ) ;
if ( ! [ fileManager removeItemAtPath : contentsPath error : nil ] ) {
writeLog ( [ @ "Failed to clear path for directory " stringByAppendingString : contentsPath ] ) ;
delFolder ( ) ;
return -1 ;
}
2014-06-25 07:25:55 +00:00
NSArray * keys = [ NSArray arrayWithObject : NSURLIsDirectoryKey ] ;
NSDirectoryEnumerator * enumerator = [ fileManager
2015-06-03 18:13:01 +00:00
enumeratorAtURL : [ NSURL fileURLWithPath : srcEnum ]
2014-06-25 07:25:55 +00:00
includingPropertiesForKeys : keys
options : 0
errorHandler : ^ ( NSURL * url , NSError * error ) {
2015-04-19 11:16:48 +00:00
writeLog ( [ [ [ @ "Error in enumerating " stringByAppendingString : [ url absoluteString ] ] stringByAppendingString : @ " error is: " ] stringByAppendingString : [ error description ] ] ) ;
2014-06-25 07:25:55 +00:00
return NO ;
} ] ;
for ( NSURL * url in enumerator ) {
NSString * srcPath = [ url path ] ;
writeLog ( [ @ "Handling file " stringByAppendingString : srcPath ] ) ;
NSRange r = [ srcPath rangeOfString : srcDir ] ;
if ( r . location ! = 0 ) {
writeLog ( [ @ "Bad file found, no base path " stringByAppendingString : srcPath ] ) ;
delFolder ( ) ;
break ;
}
NSString * pathPart = [ srcPath substringFromIndex : r . length ] ;
2014-11-27 18:20:48 +00:00
r = [ pathPart rangeOfString : appName ] ;
if ( r . location ! = 0 ) {
2014-06-25 07:25:55 +00:00
writeLog ( [ @ "Skipping not app file " stringByAppendingString : srcPath ] ) ;
continue ;
}
2014-11-27 18:20:48 +00:00
NSString * dstPath = [ appDirFull stringByAppendingString : [ pathPart substringFromIndex : r . length ] ] ;
2014-06-25 07:25:55 +00:00
NSError * error ;
NSNumber * isDirectory = nil ;
if ( ! [ url getResourceValue : & isDirectory forKey : NSURLIsDirectoryKey error : & error ] ) {
writeLog ( [ @ "Failed to get IsDirectory for file " stringByAppendingString : [ url path ] ] ) ;
delFolder ( ) ;
break ;
}
if ( [ isDirectory boolValue ] ) {
2015-06-03 18:13:01 +00:00
writeLog ( [ [ NSArray arrayWithObjects : @ "Copying dir " , srcPath , @ " to " , dstPath , nil ] componentsJoinedByString : @ "" ] ) ;
2014-06-25 07:25:55 +00:00
if ( ! [ fileManager createDirectoryAtPath : dstPath withIntermediateDirectories : YES attributes : nil error : nil ] ) {
writeLog ( [ @ "Failed to force path for directory " stringByAppendingString : dstPath ] ) ;
delFolder ( ) ;
break ;
}
2015-06-03 18:13:01 +00:00
} else if ( [ srcPath isEqualToString : readyFilePath ] ) {
writeLog ( [ [ NSArray arrayWithObjects : @ "Skipping ready file " , srcPath , nil ] componentsJoinedByString : @ "" ] ) ;
2014-06-25 07:25:55 +00:00
} else if ( [ fileManager fileExistsAtPath : dstPath ] ) {
if ( ! [ [ NSData dataWithContentsOfFile : srcPath ] writeToFile : dstPath atomically : YES ] ) {
writeLog ( [ @ "Failed to edit file " stringByAppendingString : dstPath ] ) ;
delFolder ( ) ;
break ;
}
} else {
if ( ! [ fileManager copyItemAtPath : srcPath toPath : dstPath error : nil ] ) {
writeLog ( [ @ "Failed to copy file to " stringByAppendingString : dstPath ] ) ;
delFolder ( ) ;
break ;
}
}
}
delFolder ( ) ;
}
2014-11-27 18:20:48 +00:00
NSString * appPath = [ [ NSArray arrayWithObjects : appDir , appRealName , nil ] componentsJoinedByString : @ "" ] ;
2019-01-29 10:09:37 +00:00
RemoveQuarantineFromBundle ( appPath ) ;
2017-12-12 07:52:53 +00:00
NSMutableArray * args = [ [ NSMutableArray alloc ] initWithObjects : @ "-noupdate" , nil ] ;
if ( toSettings ) [ args addObject : @ "-tosettings" ] ;
if ( _debug ) [ args addObject : @ "-debug" ] ;
if ( startInTray ) [ args addObject : @ "-startintray" ] ;
if ( testMode ) [ args addObject : @ "-testmode" ] ;
2020-05-20 00:10:21 +00:00
if ( freeType ) [ args addObject : @ "-freetype" ] ;
2018-07-11 22:14:44 +00:00
if ( externalUpdater ) [ args addObject : @ "-externalupdater" ] ;
2017-12-12 07:52:53 +00:00
if ( autoStart ) [ args addObject : @ "-autostart" ] ;
if ( key ) {
[ args addObject : @ "-key" ] ;
[ args addObject : key ] ;
}
if ( customWorkingDir ) {
[ args addObject : @ "-workdir" ] ;
[ args addObject : workDir ] ;
2014-06-25 07:25:55 +00:00
}
writeLog ( [ [ NSArray arrayWithObjects : @ "Running application '" , appPath , @ "' with args '" , [ args componentsJoinedByString : @ "' '" ] , @ "'.." , nil ] componentsJoinedByString : @ "" ] ) ;
2019-03-12 04:35:17 +00:00
for ( int i = 0 ; i < 5 ; + + i ) {
NSError * error = nil ;
NSRunningApplication * result = [ [ NSWorkspace sharedWorkspace ]
2014-06-25 07:25:55 +00:00
launchApplicationAtURL : [ NSURL fileURLWithPath : appPath ]
options : NSWorkspaceLaunchDefault
configuration : [ NSDictionary
dictionaryWithObject : args
forKey : NSWorkspaceLaunchConfigurationArguments ]
error : & error ] ;
2019-03-12 04:35:17 +00:00
if ( result ) {
closeLog ( ) ;
return 0 ;
}
2018-05-29 22:39:50 +00:00
writeLog ( [ [ NSString stringWithFormat : @ "Could not run application, error %ld: " , ( long ) [ error code ] ] stringByAppendingString : error ? [ error localizedDescription ] : @ "(nil)" ] ) ;
2019-03-12 04:35:17 +00:00
usleep ( 200000 ) ;
2014-06-25 07:25:55 +00:00
}
closeLog ( ) ;
2019-03-12 04:35:17 +00:00
return -1 ;
2014-06-25 07:25:55 +00:00
}