lnlink

iOS app for connecting to lightning nodes
git clone git://jb55.com/lnlink
Log | Files | Refs | Submodules | README | LICENSE

QR.swift (2402B)


      1 //
      2 //  QR.swift
      3 //  lightninglink
      4 //
      5 //  Created by William Casarin on 2022-03-12.
      6 //
      7 
      8 import Foundation
      9 
     10 
     11 public enum LNScanResult {
     12     case lightning(DecodeType)
     13     case lnlink(LNLink)
     14     case lnurl(URL)
     15 }
     16 
     17 
     18 func handle_qrcode(_ qr: String) -> Either<String, LNScanResult> {
     19     let invstr = qr.trimmingCharacters(in: .whitespacesAndNewlines)
     20     var lowered = invstr.lowercased()
     21 
     22     if lowered.starts(with: "lightning:") {
     23         let index = invstr.index(invstr.startIndex, offsetBy: 10)
     24         lowered = String(lowered[index...])
     25     }
     26 
     27     if lowered.starts(with: "lnlink:") {
     28         switch parse_auth_qr(invstr) {
     29         case .left(let err):
     30             return .left(err)
     31         case .right(let lnlink):
     32             return .right(.lnlink(lnlink))
     33         }
     34     }
     35 
     36     if lowered.starts(with: "lnurl") {
     37         guard let bech32 = decode_bech32(lowered) else {
     38             return .left("Invalid lnurl bech32 encoding")
     39         }
     40 
     41         let url = String(decoding: bech32.dat, as: UTF8.self)
     42         if let email = parse_email(str: url) {
     43             guard let lnurl = make_lnaddress(email: email) else {
     44                 return .left("Couldn't make lnaddress from email")
     45             }
     46             return .right(.lnurl(lnurl))
     47         }
     48 
     49         guard let lnurl = URL(string: url.trimmingCharacters(in: .whitespacesAndNewlines)) else {
     50             return .left("Couldn't make lnurl from qr")
     51         }
     52 
     53         return .right(.lnurl(lnurl))
     54     }
     55 
     56     if let email = parse_email(str: lowered) {
     57         guard let lnurl = make_lnaddress(email: email) else {
     58             return .left("Couldn't make lnaddress from email")
     59         }
     60         return .right(.lnurl(lnurl))
     61     }
     62 
     63     guard let parsed = parseInvoiceString(lowered) else {
     64         return .left("Failed to parse invoice")
     65     }
     66 
     67     return .right(.lightning(parsed))
     68 }
     69 
     70 
     71 struct Email {
     72     let name: String
     73     let host: String
     74 }
     75 
     76 func parse_email(str: String) -> Email? {
     77     let parts = str.split(separator: "@")
     78 
     79     guard parts.count == 2 else {
     80         return nil
     81     }
     82 
     83     if parts[0].contains(":") {
     84         return nil
     85     }
     86 
     87     if !parts[1].contains(".") {
     88         return nil
     89     }
     90 
     91     let name = String(parts[0])
     92     let host = String(parts[1])
     93 
     94     return Email(name: name, host: host)
     95 }
     96 
     97 
     98 func make_lnaddress(email: Email) -> URL? {
     99     return URL(string: "https://\(email.host)/.well-known/lnurlp/\(email.name)")
    100 }