* (feat: new tool): add wifi qr code generator * Update src/tools/wifi-qr-code-generator/wifi-qr-code-generator.vue Co-authored-by: Corentin THOMASSET <corentin.thomasset74@gmail.com> * Update src/tools/wifi-qr-code-generator/index.ts Co-authored-by: Corentin THOMASSET <corentin.thomasset74@gmail.com> * remove naive UI grid * Update src/tools/wifi-qr-code-generator/index.ts --------- Co-authored-by: Corentin THOMASSET <corentin.thomasset74@gmail.com>
		
			
				
	
	
		
			147 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { type MaybeRef, get } from '@vueuse/core';
 | |
| import QRCode, { type QRCodeToDataURLOptions } from 'qrcode';
 | |
| import { isRef, ref, watch } from 'vue';
 | |
| 
 | |
| export const wifiEncryptions = ['WEP', 'WPA', 'nopass', 'WPA2-EAP'] as const;
 | |
| export type WifiEncryption = typeof wifiEncryptions[number];
 | |
| 
 | |
| // @see https://en.wikipedia.org/wiki/Extensible_Authentication_Protocol
 | |
| // for a list of available EAP methods. There are a lot (40!) of them.
 | |
| export const EAPMethods = [
 | |
|   'MD5',
 | |
|   'POTP',
 | |
|   'GTC',
 | |
|   'TLS',
 | |
|   'IKEv2',
 | |
|   'SIM',
 | |
|   'AKA',
 | |
|   'AKA\'',
 | |
|   'TTLS',
 | |
|   'PWD',
 | |
|   'LEAP',
 | |
|   'PSK',
 | |
|   'FAST',
 | |
|   'TEAP',
 | |
|   'EKE',
 | |
|   'NOOB',
 | |
|   'PEAP',
 | |
| ] as const;
 | |
| export type EAPMethod = typeof EAPMethods[number];
 | |
| 
 | |
| export const EAPPhase2Methods = [
 | |
|   'None',
 | |
|   'MSCHAPV2',
 | |
| ] as const;
 | |
| export type EAPPhase2Method = typeof EAPPhase2Methods[number];
 | |
| 
 | |
| interface IWifiQRCodeOptions {
 | |
|   ssid: MaybeRef<string>
 | |
|   password: MaybeRef<string>
 | |
|   eapMethod: MaybeRef<EAPMethod>
 | |
|   isHiddenSSID: MaybeRef<boolean>
 | |
|   eapAnonymous: MaybeRef<boolean>
 | |
|   eapIdentity: MaybeRef<string>
 | |
|   eapPhase2Method: MaybeRef<EAPPhase2Method>
 | |
|   color: { foreground: MaybeRef<string>; background: MaybeRef<string> }
 | |
|   options?: QRCodeToDataURLOptions
 | |
| }
 | |
| 
 | |
| interface GetQrCodeTextOptions {
 | |
|   ssid: string
 | |
|   password: string
 | |
|   encryption: WifiEncryption
 | |
|   eapMethod: EAPMethod
 | |
|   isHiddenSSID: boolean
 | |
|   eapAnonymous: boolean
 | |
|   eapIdentity: string
 | |
|   eapPhase2Method: EAPPhase2Method
 | |
| }
 | |
| 
 | |
| function escapeString(str: string) {
 | |
|   // replaces \, ;, ,, " and : with the same character preceded by a backslash
 | |
|   return str.replace(/([\\;,:"])/g, '\\$1');
 | |
| }
 | |
| 
 | |
| function getQrCodeText(options: GetQrCodeTextOptions): string | null {
 | |
|   const { ssid, password, encryption, eapMethod, isHiddenSSID, eapAnonymous, eapIdentity, eapPhase2Method } = options;
 | |
|   if (!ssid) {
 | |
|     return null;
 | |
|   }
 | |
|   if (encryption === 'nopass') {
 | |
|     return `WIFI:S:${escapeString(ssid)};;`; // type can be omitted in that case, and password is not needed, makes the QR Code smaller
 | |
|   }
 | |
|   if (encryption !== 'WPA2-EAP' && password) {
 | |
|     // EAP has a lot of options, so we'll handle it separately
 | |
|     // WPA and WEP are pretty simple though.
 | |
|     return `WIFI:S:${escapeString(ssid)};T:${encryption};P:${escapeString(password)};${isHiddenSSID ? 'H:true' : ''};`;
 | |
|   }
 | |
|   if (encryption === 'WPA2-EAP' && password && eapMethod) {
 | |
|     // WPA2-EAP string is a lot more complex, first off, we drop the text if there is no identity, and it's not anonymous.
 | |
|     if (!eapIdentity && !eapAnonymous) {
 | |
|       return null;
 | |
|     }
 | |
|     // From reading, I could only find that a phase 2 is required for the PEAP method, I may be wrong though, I didn't read the whole spec.
 | |
|     if (eapMethod === 'PEAP' && !eapPhase2Method) {
 | |
|       return null;
 | |
|     }
 | |
|     // The string is built in the following order:
 | |
|     // 1. SSID
 | |
|     // 2. Authentication type
 | |
|     // 3. Password
 | |
|     // 4. EAP method
 | |
|     // 5. EAP phase 2 method
 | |
|     // 6. Identity or anonymous if checked
 | |
|     // 7. Hidden SSID if checked
 | |
|     const identity = eapAnonymous ? 'A:anon' : `I:${escapeString(eapIdentity)}`;
 | |
|     const phase2 = eapPhase2Method !== 'None' ? `PH2:${eapPhase2Method};` : '';
 | |
|     return `WIFI:S:${escapeString(ssid)};T:WPA2-EAP;P:${escapeString(password)};E:${eapMethod};${phase2}${identity};${isHiddenSSID ? 'H:true' : ''};`;
 | |
|   }
 | |
|   return null;
 | |
| }
 | |
| 
 | |
| export function useWifiQRCode({
 | |
|   ssid,
 | |
|   password,
 | |
|   eapMethod,
 | |
|   isHiddenSSID,
 | |
|   eapAnonymous,
 | |
|   eapIdentity,
 | |
|   eapPhase2Method,
 | |
|   color: { background, foreground },
 | |
|   options,
 | |
| }: IWifiQRCodeOptions) {
 | |
|   const qrcode = ref('');
 | |
|   const encryption = ref<WifiEncryption>('WPA');
 | |
| 
 | |
|   watch(
 | |
|     [ssid, password, encryption, eapMethod, isHiddenSSID, eapAnonymous, eapIdentity, eapPhase2Method, background, foreground].filter(isRef),
 | |
|     async () => {
 | |
|       // @see https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11
 | |
|       // This is the full spec, there's quite a bit of logic to generate the string embeddedin the QR code.
 | |
|       const text = getQrCodeText({
 | |
|         ssid: get(ssid),
 | |
|         password: get(password),
 | |
|         encryption: get(encryption),
 | |
|         eapMethod: get(eapMethod),
 | |
|         isHiddenSSID: get(isHiddenSSID),
 | |
|         eapAnonymous: get(eapAnonymous),
 | |
|         eapIdentity: get(eapIdentity),
 | |
|         eapPhase2Method: get(eapPhase2Method),
 | |
|       });
 | |
|       if (text) {
 | |
|         qrcode.value = await QRCode.toDataURL(get(text).trim(), {
 | |
|           color: {
 | |
|             dark: get(foreground),
 | |
|             light: get(background),
 | |
|             ...options?.color,
 | |
|           },
 | |
|           errorCorrectionLevel: 'M',
 | |
|           ...options,
 | |
|         });
 | |
|       }
 | |
|     },
 | |
|     { immediate: true },
 | |
|   );
 | |
|   return { qrcode, encryption };
 | |
| }
 |