@@ -25,6 +25,19 @@ func printUsage() {
2525 )
2626}
2727
28+ func printHelp( ) {
29+ let command = URL ( fileURLWithPath: CommandLine . arguments. first!) . lastPathComponent;
30+ print (
31+ " Usage: \( command) [OPTIONS] [ARCHIVES_FOLDER] \n " ,
32+ " Options: \n " ,
33+ " \t -f: provide the path to the private DSA key \n " ,
34+ " \t -n: provide the name of the private DSA key. This option has to be used together with `-k` \n " ,
35+ " \t -k: provide the name of the keychain. This option has to be used together with `-n` \n " ,
36+ " \t -s: provide the path to the private EdDSA key \n " ,
37+ " \t --download-url-prefix: provide a static url that will be used as prefix for the url from where updates will be downloaded \n "
38+ )
39+ }
40+
2841func loadPrivateKeys( _ privateDSAKey: SecKey ? , _ privateEdString: String ? ) -> PrivateKeys {
2942 var privateEdKey : Data ? ;
3043 var publicEdKey : Data ? ;
@@ -62,75 +75,157 @@ func loadPrivateKeys(_ privateDSAKey: SecKey?, _ privateEdString: String?) -> Pr
6275 return PrivateKeys ( privateDSAKey: privateDSAKey, privateEdKey: privateEdKey, publicEdKey: publicEdKey) ;
6376}
6477
65- func main( ) {
66- let args = CommandLine . arguments;
67- if args. count < 2 {
68- printUsage ( )
78+ /**
79+ * Parses all possible command line options and returns the values in a tuple.
80+ */
81+ func parseCommandLineOptions( argumentList: [ String ] ) -> ( privateDSAKey: SecKey ? , privateEdString: String ? , downloadUrlPrefix: URL ? , archivesSourceDir: URL ) {
82+ // if the option `-h` is in the argument list print the help dialog
83+ if argumentList. contains ( " -h " ) {
84+ printHelp ( )
6985 exit ( 1 )
7086 }
7187
72- var privateDSAKey : SecKey ? = nil ;
73- var privateEdString : String ? = nil ;
74-
75- // this was typical usage for DSA keys
76- if args. count == 3 || ( args. count == 4 && args [ 1 ] == " -f " ) {
77- // private key specified by filename
78- let privateKeyURL = URL ( fileURLWithPath: args. count == 3 ? args [ 1 ] : args [ 2 ] )
88+ // make a mutable copy of the argument list
89+ var arguments = argumentList
90+ // remove the first element since this is the path to executable which we don't need
91+ arguments. removeFirst ( )
92+
93+ // define the variables for the possible argument values
94+ var privateDSAKey : SecKey ? = nil
95+ var privateEdString : String ? = nil
96+ var downloadUrlPrefix : URL ? = nil
97+ var archivesSourceDir : URL
98+
99+ // check if the private dsa key option is present
100+ if let privateDSAKeyOptionIndex = arguments. firstIndex ( of: " -f " ) {
101+ // check that when accessing the value of the option we don't get out of bounds
102+ if privateDSAKeyOptionIndex + 1 >= arguments. count {
103+ print ( " Too few arguments were given " )
104+ exit ( 1 )
105+ }
79106
107+ // get the private DSA key
108+ let privateKeyUrl = URL ( fileURLWithPath: arguments [ privateDSAKeyOptionIndex + 1 ] )
80109 do {
81- privateDSAKey = try loadPrivateDSAKey ( at: privateKeyURL )
110+ privateDSAKey = try loadPrivateDSAKey ( at: privateKeyUrl )
82111 } catch {
83- print ( " Unable to load DSA private key from " , privateKeyURL . path, " \n " , error)
112+ print ( " Unable to load DSA private key from " , privateKeyUrl . path, " \n " , error)
84113 exit ( 1 )
85114 }
115+
116+ // remove the already parsed arguments
117+ arguments. remove ( at: privateDSAKeyOptionIndex)
118+ arguments. remove ( at: privateDSAKeyOptionIndex + 1 )
86119 }
87- // this is legacy for DSA keychain; probably very rarely used
88- else if args. count == 6 && ( args [ 1 ] == " -n " || args [ 1 ] == " -k " ) {
89- // private key specified by keychain + key name
90- let keyName : String
91- let keychainURL : URL
120+
121+ // check if the private dsa sould be loaded using the keyname and the name of the keychain
122+ if let keyNameOptionIndex = arguments. firstIndex ( of: " -n " ) , let keychainNameOptionIndex = arguments. firstIndex ( of: " -k " ) {
123+ // check that when accessing one of the values of the options we don't get out of bounds
124+ if keyNameOptionIndex + 1 >= arguments. count || keychainNameOptionIndex + 1 >= arguments. count {
125+ print ( " Too few arguments were given " )
126+ exit ( 1 )
127+ }
92128
93- if args [ 1 ] == " -n " {
94- if args [ 3 ] != " -k " {
95- printUsage ( )
96- exit ( 1 )
97- }
98-
99- keyName = args [ 2 ]
100- keychainURL = URL ( fileURLWithPath : args [ 4 ] )
129+ // get the keyname and the keychain url to load the private DSA key
130+ let keyName : String = arguments [ keyNameOptionIndex + 1 ]
131+ let keychainUrl : URL = URL ( fileURLWithPath : arguments [ keychainNameOptionIndex + 1 ] )
132+ do {
133+ privateDSAKey = try loadPrivateDSAKey ( named : keyName , fromKeychainAt : keychainUrl )
134+ } catch {
135+ print ( " Unable to load DSA private key ' \( keyName) ' from keychain at " , keychainUrl . path , " \n " , error )
136+ exit ( 1 )
101137 }
102- else {
103- if args [ 3 ] != " -n " {
104- printUsage ( )
105- exit ( 1 )
106- }
107-
108- keyName = args [ 4 ]
109- keychainURL = URL ( fileURLWithPath: args [ 2 ] )
138+
139+ // remove the already parsed arguments
140+ arguments. remove ( at: keyNameOptionIndex + 1 )
141+ arguments. remove ( at: keyNameOptionIndex)
142+ arguments. remove ( at: arguments. firstIndex ( of: " -k " ) ! + 1 )
143+ arguments. remove ( at: keychainNameOptionIndex)
144+ }
145+
146+ // check if the private EdDSA key string was given as an argument
147+ if let privateEdDSAKeyOptionIndex = arguments. firstIndex ( of: " -s " ) {
148+ // check that when accessing the value of the option we don't get out of bounds
149+ if privateEdDSAKeyOptionIndex + 1 >= arguments. count {
150+ print ( " Too few arguments were given " )
151+ exit ( 1 )
110152 }
111-
153+
154+ // get the private EdDSA key string
155+ privateEdString = arguments [ privateEdDSAKeyOptionIndex + 1 ]
156+
157+ // remove the already parsed argument
158+ arguments. remove ( at: privateEdDSAKeyOptionIndex + 1 )
159+ arguments. remove ( at: privateEdDSAKeyOptionIndex)
160+ }
161+
162+ // check if a prefix for the download url of the archives was given
163+ if let downloadUrlPrefixOptionIndex = arguments. firstIndex ( of: " --download-url-prefix " ) {
164+ // check that when accessing the value of the option we don't get out of bounds
165+ if downloadUrlPrefixOptionIndex + 1 >= arguments. count {
166+ print ( " Too few arguments were given " )
167+ exit ( 1 )
168+ }
169+
170+ // get the download url prefix
171+ downloadUrlPrefix = URL ( string: arguments [ downloadUrlPrefixOptionIndex + 1 ] )
172+
173+ // remove the parsed argument
174+ arguments. remove ( at: downloadUrlPrefixOptionIndex + 1 )
175+ arguments. remove ( at: downloadUrlPrefixOptionIndex)
176+ }
177+
178+ // now that all command line options have been removed from the arguments array
179+ // there should only be the path to the private DSA key (if provided) path to the archives dir left
180+ if arguments. count == 2 {
181+ // if there are two arguments left they are the private DSA key and the path to the archives directory (in this order)
182+ // first get the private DSA key
183+ let privateKeyUrl = URL ( fileURLWithPath: arguments [ 0 ] )
112184 do {
113- privateDSAKey = try loadPrivateDSAKey ( named : keyName , fromKeychainAt : keychainURL )
185+ privateDSAKey = try loadPrivateDSAKey ( at : privateKeyUrl )
114186 } catch {
115- print ( " Unable to load DSA private key ' \( keyName ) ' from keychain at " , keychainURL . path, " \n " , error)
187+ print ( " Unable to load DSA private key from " , privateKeyUrl . path, " \n " , error)
116188 exit ( 1 )
117189 }
190+
191+ // remove the parsed path to the DSA key
192+ arguments. removeFirst ( )
118193 }
119- // private + public EdDSA keys are provided via command line argument
120- else if args. count == 4 && args [ 1 ] == " -s " {
121- privateEdString = args [ 2 ]
122- }
123- else if args. count != 2 {
194+
195+ // now only the archives source dir is left
196+ archivesSourceDir = URL ( fileURLWithPath: arguments [ 0 ] , isDirectory: true )
197+
198+ return ( privateDSAKey, privateEdString, downloadUrlPrefix, archivesSourceDir)
199+ }
200+
201+ func main( ) {
202+ let args = CommandLine . arguments;
203+ if args. count < 2 {
124204 printUsage ( )
125205 exit ( 1 )
126206 }
127207
128- let archivesSourceDir = URL ( fileURLWithPath: args. last!, isDirectory: true )
208+ var privateDSAKey : SecKey ? = nil ;
209+ var privateEdString : String ? = nil ;
210+ var downloadUrlPrefix : URL ? = nil ;
211+ var archivesSourceDir : URL
212+
213+ ( privateDSAKey, privateEdString, downloadUrlPrefix, archivesSourceDir) = parseCommandLineOptions ( argumentList: args)
214+
129215 let keys = loadPrivateKeys ( privateDSAKey, privateEdString)
130216
131217 do {
132218 let allUpdates = try makeAppcast ( archivesSourceDir: archivesSourceDir, keys: keys, verbose: verbose) ;
133219
220+ // if a download url prefix was provided set it for each archive item
221+ if downloadUrlPrefix != nil {
222+ for (_, archiveItems) in allUpdates {
223+ for archiveItem in archiveItems {
224+ archiveItem. downloadUrlPrefix = downloadUrlPrefix
225+ }
226+ }
227+ }
228+
134229 for (appcastFile, updates) in allUpdates {
135230 let appcastDestPath = URL ( fileURLWithPath: appcastFile, relativeTo: archivesSourceDir) ;
136231 try writeAppcast ( appcastDestPath: appcastDestPath, updates: updates) ;
0 commit comments