// License: BSD/LGPL
// Copyright (C) 2011 Thomas d'Otreppe
using System;
using System.Collections.Generic;
namespace WirelessPanda.Readers
{
public class CsvReader : Reader
{
///
/// Date format (Same format for 0.x and 1.x)
///
protected override string DATE_FORMAT
{
get
{
return "yyyy-MM-dd HH:mm:ss";
}
}
public enum CSVFileFormat
{
v0X,
v1X,
Unknown
}
///
/// Get the file format
///
public CSVFileFormat FileFormat
{
get
{
return this._fileFormat;
}
}
private CSVFileFormat _fileFormat = CSVFileFormat.Unknown;
///
/// Reader type
///
public override string ReaderType
{
get
{
return "Airodump-ng CSV";
}
}
///
/// Constructor
///
/// Filename (doesn't need to exist now but MUST when using Read() )
public CsvReader(string filename) : base(filename) { }
///
/// Read/Update the content of the file
///
/// true if successful
/// Airodump-ng CSV format unknown
public override bool Read()
{
// Reset parsing status
this.ParseSuccess = false;
// Get the content of the file
string[] content = this.getStrippedFileContent();
// Get file format
this._fileFormat = this.getFormat(content);
if (this._fileFormat == CSVFileFormat.Unknown)
{
throw new FormatException("Airodump-ng CSV format unknown");
}
// Parse AP ...
int i = 2; // Start at line 3 (skipping header)
for (; i < content.Length && !string.IsNullOrEmpty(content[i]); i++)
{
string [] splitted = content[i].Split(',');
switch (this._fileFormat)
{
case CSVFileFormat.v0X:
if (splitted.Length < 11)
{
continue;
}
break;
case CSVFileFormat.v1X:
if (splitted.Length < 15)
{
continue;
}
break;
}
AccessPoint ap = new AccessPoint();
ap.BSSID = splitted[0].Trim();
ap.FirstTimeSeen = this.parseDateTime(splitted[1]);
ap.LastTimeSeen = this.parseDateTime(splitted[2]);
ap.Channel = int.Parse(splitted[3].Trim());
ap.MaxRate = double.Parse(splitted[4].Trim());
ap.Privacy = splitted[5].Trim();
switch (this._fileFormat)
{
case CSVFileFormat.v0X:
ap.Power = int.Parse(splitted[6].Trim());
ap.Beacons = long.Parse(splitted[7].Trim());
ap.DataFrames = ulong.Parse(splitted[8].Trim());
ap.IP = splitted[9].Replace(" ", "");
ap.ESSID = splitted[10].Substring(1); // TODO: Improve it because it may contain a ','
ap.ESSIDLength = (byte)ap.ESSID.Length;
break;
case CSVFileFormat.v1X:
ap.Cipher = splitted[6].Trim();
ap.Authentication = splitted[7].Trim();
ap.Power = int.Parse(splitted[8].Trim());
ap.Beacons = long.Parse(splitted[9].Trim());
ap.DataFrames = ulong.Parse(splitted[10].Trim());
ap.IP = splitted[11].Replace(" ", "");
ap.ESSIDLength = byte.Parse(splitted[12].Trim());
ap.ESSID = splitted[13].Substring(1); // TODO: Improve it because it may contain a ','
ap.Key = splitted[14];
break;
}
// Add AP to the list
this.addAccessPoint(ap);
}
// ... Parse stations
i += 2; // Skip station header
for (; i < content.Length && !string.IsNullOrEmpty(content[i]); i++)
{
string[] splitted = content[i].Split(',');
// Skip to the next if not long enough
if (splitted.Length < 6)
{
continue;
}
// Parse station information
Station sta = new Station();
sta.StationMAC = splitted[0].Trim();
sta.FirstTimeSeen = this.parseDateTime(splitted[1]);
sta.LastTimeSeen = this.parseDateTime(splitted[2]);
sta.Power = int.Parse(splitted[3].Trim());
sta.NbPackets = ulong.Parse(splitted[4].Trim());
sta.BSSID = splitted[5].Trim();
// Get probed ESSID list
if (splitted.Length > 6 && splitted[6] != "")
{
List list = new List();
for (int j = 6; j < splitted.Length; j++)
{
// There's always a whitespace character before
list.Add(splitted[j].Substring(1));
}
sta.ProbedESSIDsList = list.ToArray();
}
else
{
sta.ProbedESSIDs = string.Empty;
}
// Add station to the list
this.addStation(sta);
}
// Link them together
this.LinkAPClients();
// Parsing was successful
this.ParseSuccess = true;
return this.ParseSuccess;
}
///
/// Returns the format of the file
///
/// File content
/// CSV File Format
/// content is null
/// content is empty
private CSVFileFormat getFormat(string[] content)
{
// Checks
if (content == null)
{
throw new ArgumentNullException("Cannot determine format without any content");
}
if (content.Length == 1 && string.IsNullOrEmpty(content[0]))
{
throw new ArgumentException("Cannot determine format without any content");
}
// First line is empty and the second line contains the header
if (content.Length > 2 && string.IsNullOrEmpty(content[0]))
{
// Version 1.x
if (content[1] == "BSSID, First time seen, Last time seen, channel, Speed, Privacy, Cipher, Authentication, Power, # beacons, # IV, LAN IP, ID-length, ESSID, Key")
{
return CSVFileFormat.v1X;
}
// Version 0.x
if (content[1] == "BSSID, First time seen, Last time seen, Channel, Speed, Privacy, Power, # beacons, # data, LAN IP, ESSID")
{
return CSVFileFormat.v0X;
}
}
return CSVFileFormat.Unknown;
}
}
}