Search code examples
swiftmacosencodingsudo

Problems when passing password containing umlauts to Process as argument in Swift


So I am trying to execute a command using sudo from Swift using the following code (as suggested here:

func doTask(_ password:String) {
    let taskOne = Process()
    taskOne.launchPath = "/bin/echo"
    taskOne.arguments = [password]

    let taskTwo = Process()
    taskTwo.launchPath = "/usr/bin/sudo"
    taskTwo.arguments = ["-S", "/usr/bin/xattr", "-d", "-r", "com.test.exemple", " /Desktop/file.extension"]
    //taskTwo.arguments = ["-S", "/usr/bin/touch", "/tmp/foo.bar.baz"]

    let pipeBetween:Pipe = Pipe()
    taskOne.standardOutput = pipeBetween
    taskTwo.standardInput = pipeBetween

    let pipeToMe = Pipe()
    taskTwo.standardOutput = pipeToMe
    taskTwo.standardError = pipeToMe

    taskOne.launch()
    taskTwo.launch()

    let data = pipeToMe.fileHandleForReading.readDataToEndOfFile()
    let output : String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
    print(output)
}

It works just fine for passwords such as "test", "password#123" etc. But when I try a password containing an umlaut such as "ä","ü" or "ö" in doesn't work. Any ideas why?


Solution

  • I'm not sure why the answer to the other question piped through echo... seems to introduce unnecessary complications and unknowns.

    The following more direct approach is tested and working:

    import Foundation
    
    let password = "äëïöü"
    let passwordWithNewline = password + "\n"
    let sudo = Process()
    sudo.launchPath = "/usr/bin/sudo"
    sudo.arguments = ["-S", "/bin/ls"]
    let sudoIn = Pipe()
    let sudoOut = Pipe()
    sudo.standardOutput = sudoOut
    sudo.standardError = sudoOut
    sudo.standardInput = sudoIn
    sudo.launch()
    
    // Show the output as it is produced
    sudoOut.fileHandleForReading.readabilityHandler = { fileHandle in
        let data = fileHandle.availableData
        if (data.count == 0) { return }
        print("read \(data.count)")
        print("\(String(bytes: data, encoding: .utf8) ?? "<UTF8 conversion failed>")")
    
    }
    // Write the password
    sudoIn.fileHandleForWriting.write(passwordWithNewline.data(using: .utf8)!)
    
    // Close the file handle after writing the password; avoids a
    // hang for incorrect password.
    try? sudoIn.fileHandleForWriting.close()
    
    // Make sure we don't disappear while output is still being produced.
    sudo.waitUntilExit()
    print("Process did exit")
    

    The crux is that you must add a newline after the password. (I suppose in some ways echo is just an overly complicated way of doing that!)