DAmn/SampleParser

From Botdom Documentation
Jump to: navigation, search

Here are some examples of dAmn packet parsers written in different programming languages.

C#

This one was written by plaguethenet, parses packets the exact same way the official client does it.

<csharp>

   public class dAmnArgs
   {
       private string _body;
       private Dictionary<string, string> _args;
       public Dictionary<string, string> args
       {
           get { return _args; }
           set { _args = value; }
       }
       public string body
       {
           get { return _body; }
           set { _body = value; }
       }
       public static dAmnArgs getArgsNData(string data)
       {
           dAmnArgs p = new dAmnArgs();
           p.args = new Dictionary<string, string>();
           p.body = null;
           while (true)
           {
               if (data.Length == 0 || data[0] == '\n')
                   break;
               int i = data.IndexOf('\n');
               int j = data.IndexOf('=');
               if (j > i)
                   break;
               p.args[data.Substring(0, j)] = data.Substring(j + 1, i - (j + 1));
               data = data.Substring(i + 1);
           }
           if (data != null && data.Length > 0)
               p.body = data.Substring(1);
           else
               p.body = "";
           return p;
       }
   }
   public class dAmnPacket
   {
       private string _cmd = "";
       private string _param = "";
       //private string _body = "";
       //private Dictionary<string, string> _args = new Dictionary<string,string>();
       dAmnArgs argsAndBody;
       public string cmd
       {
           get { return _cmd; }
           set { _cmd = value; }
       }
       public string param
       {
           get { return _param; }
           set { _param = value; }
       }
       public string body
       {
           get { return argsAndBody.body; }
           set { argsAndBody.body = value; }
       }
       public Dictionary<string, string> args
       {
           get { return argsAndBody.args; }
           set { argsAndBody.args = value; }
       }
       public static dAmnPacket parse(string data)
       {
           if (data == null)
               throw new ArgumentNullException("data");
           string orig_data = data;
           try
           {
               dAmnPacket p = new dAmnPacket();
               int i = data.IndexOf('\n');
               if (i < 0)
                   throw new Exception("Parser error, No line break.");
               string tmp = data.Substring(0, i);
               p.cmd = tmp.Split(' ')[0];
               int j = tmp.IndexOf(' ');
               if (j > 0)
                   p.param = tmp.Substring(j + 1);
               p.argsAndBody = dAmnArgs.getArgsNData(data.Substring(i + 1));
               return p;
           }
           catch (Exception e)
           {
               throw e;
           }
           //return null; //wtf? how did we get here? D:
       }
   }

</csharp>

It is very easy to use this parser:

<csharp> dAmnPacket p = dAmnPacket.parse(data); </csharp>

For the recv packet you can just parse the body again to extract the sub packet.





This is a dAmnPacket class used for both parsing and creating a packet in C# by Kyogo

<csharp>using System; using System.Collections.Generic;

namespace lulzbot.Networking {

   /// <summary>
   /// This will be our dAmnPacket class. It will basically be an object that we
   ///  pass data to, and it will process the data and create a viable packet object
   ///  from it. From there on, we can use the object to handle events and respond.
   /// </summary>
   public class dAmnPacket
   {
       // Here are our public properties, which will hold our important data!
       
       // This shouldn't ever change in dAmn, but who knows. It's the separator between
       //  argument names and their data.
       // This is flagged "const" (a constant) so that it cannot be changed. It's also
       //  static by default, and is easily optimized by the compiler.
       private const String Separator = "=";
       // I prefer to initialize strings with the String.Empty value (""). This seems
       //  neater to me, and avoids errors if you somehow add to/use them before assignment.
       public String Command       = String.Empty;
       public String Parameter     = String.Empty;
       public String SubCommand    = String.Empty;
       public String SubParameter  = String.Empty;
       public String Body          = String.Empty;
       public String Raw           = String.Empty;
       // For the arguments, we'll use a dictionary. While, in the past, I used different types
       //  to handle duplicate keys, I'm not going to. Why? dAmn should _not_ send duplicate argument
       //  names.
       public Dictionary<String, String> Arguments = new Dictionary<String, String>();
       /// <summary>
       /// Here, we'll parse the string data of a packet and create our object.
       /// </summary>
       /// <param name="data">The string data of a packet.</param>
       public dAmnPacket(String data)
       {
           if (data.Contains("\n"))
           {
               int pos = data.IndexOf("\n");
               String header = data.Substring(0, pos);
               if (header.Contains(" "))
               {
                   String[] bits   = header.Split(' ');
                   Command         = bits[0];
                   Parameter       = bits[1];
               }
               else
               {
                   Command = header;
               }
               data = data.Substring(pos + 1);
               if (data.Contains("\n\n"))
               {
                   pos  = data.IndexOf("\n\n");
                   // We'll parse tablumps here
                   Body = Tools.ParseTablumps(data.Substring(pos + 2));
                   data = data.Substring(0, pos);
               }
               foreach (String chunk in data.Split('\n'))
               {
                   if (String.IsNullOrWhiteSpace(chunk))
                   {
                       // Don't bother with empty chunks!
                       continue;
                   }
                   if (chunk.Contains(Separator))
                   {
                       String argument     = chunk.Substring(0, chunk.IndexOf(Separator));
                       String value        = chunk.Substring(chunk.IndexOf(Separator) + 1);
                       Arguments.Add(argument, value);
                   }
                   else
                   {
                       if (String.IsNullOrWhiteSpace(SubCommand))
                       {
                           if (chunk.Contains(" "))
                           {
                               String[] bits   = chunk.Split(' ');
                               SubCommand      = bits[0];
                               SubParameter    = bits[1];
                           }
                           else
                           {
                               SubCommand      = chunk;
                           }
                       }
                       else
                       {
                           // Shouldn't happen
                       }
                   }
               }
           }
       }
       /// <summary>
       /// Some packets contain arguments in the body. This function pulls them and adds them to Arguments.
       /// </summary>
       public void PullBodyArguments()
       {
           if (!Body.Contains("\n") || !Body.Contains(Separator))
               return;
           foreach (String chunk in Body.Split('\n'))
           {
               if (String.IsNullOrWhiteSpace(chunk))
               {
                   // Don't bother with empty chunks!
                   continue;
               }
               if (chunk.Contains(Separator))
               {
                   String argument = chunk.Substring(0, chunk.IndexOf(Separator));
                   String value = chunk.Substring(chunk.IndexOf(Separator) + 1);
                   Arguments.Add(argument, value);
               }
           }
       }
   }

} </csharp>

PHP

This is dbpc and xbot's dAmn packet parser. I added this here because ManjyomeThunder complained that all the other ones on this page were too complicated/slow/long.

You'll need this constant defined: <php> define('LF', chr(10)); </php>

Parser: <php> // converts a dAmn raw string to a dAmn packet data structure function str2dpkt($raw) { list($head, $body) = args($raw, 2, LF.LF); list($hair, $face) = args($head, 2, LF); $pkt = args($hair, 2); $pkt[2] = arghasht($face); $pkt[3] = $body; return $pkt; } </php> It uses two customs function, args() and arghasht(), included with the clients, but if you have no need of their extra functionality, these simpler ones will work fine. <php> function args($data, $r=0, $sep=' ') { $args = explode($sep,$data,$r); if (count($args)<$r) { $args = array_pad($args,$r,); } return $args; } </php> <php> function arghasht($data, $sep='=', $linesep=LF) { if (empty($data)) { return array(); } // most of the time this will be the case $lines = explode($linesep, $data); foreach ($lines as $line) { if (empty($line)) { continue; } list($k, $v) = args($line, 2, $sep); $hasht[$k] = $v; } return $hasht; } </php> And here's the inverse function: <php> // converts a dAmn packet data structure to a dAmn raw string function dpkt2str($pkt) { $str = empty($pkt[1])? $pkt[0].LF: $pkt[0].' '.$pkt[1].LF; if (isset($pkt[2])) { foreach ($pkt[2] as $k=>$v) { $str .= $k.'='.$v.LF; } } if (!empty($pkt[3])) { $str .= LF.$pkt[3]; } return $str; } </php>


This one was written by photofroggy, official parser was not referred to when making it. Uses a much more simple concept. ManjyomeThunder helped design this version.

<php> function parse_dAmn_packet($data, $sep = '=') {

$packet = array( 'cmd' => Null, 'param' => Null, 'args' => array(), 'body' => Null, 'raw' => $data ); if(stristr($data, "\n\n")) { $packet['body'] = trim(stristr($data, "\n\n")); $data = substr($data, 0, strpos($data, "\n\n")); } $data = explode("\n", $data); foreach($data as $id => $str) { if(strpos($str, $sep) != 0) { $packet['args'][substr($str, 0, strpos($str, $sep))] = substr($str, strpos($str, $sep)+1); } elseif(strlen($str) >=1) { if($id == 0) { if(!stristr($str, ' ')) { $packet['cmd'] = $str; } else { $packet['cmd'] = substr($str, 0, strpos($str, ' ')); $packet['param'] = trim(stristr($str, ' ')); } } else { $packet['args'][] = $str; } } } return $packet;

} </php>

Using this parser is also easy: <php> $packet = parse_dAmn_packet($data);

  1. Example of usage:

$packet = parse_dAmn_packet("command parameter\narg=value\narg2=value2\n\nBody");

  1. This would result in the following:
  2. $packet['cmd'] = 'command';
  3. $packet['param'] = 'parameter';
  4. $packet['args'] = array(
  5. 'arg' => 'value',
  6. 'arg2'=> 'value2'
  7. );
  8. $packet['body'] = 'Body';
  9. $packet['raw'] = 'command parameter\narg=value\narg2=value2\n\nBody';

</php> For the recv packet you can just parse the body again to extract the sub packet.

Python

Ported from plaguethenet's code by SubjectX52873M <python> import UserDict

class Packet(UserDict.DictMixin):

def __init__(self,data=None,subPacket=False): """if a string is passed the string is parsed as a dAmnserver packet otherwise creates an empty packet object""" self.cmd="" self.param=False self.body=False self.__args={} if data!=None: data=str(data) try: i = data.find("\n") if i==-1: raise DamnParserException,"Parser error, No line break." tmp = data[0:i].split(' ',1) self.cmd = tmp[0] if len(tmp)==2: self.param=tmp[1] data=data[(i+1):] while True: if len(data) == 0 or data[0] == "\n": break; i = data.find("\n") j = data.find('=') if i==-1 and j!=-1: r[data[0:j]]=data[(j+1):] elif j > i: break; else: self.__args[data[0:j]]=data[(j+1):i] data=data[i:] #EndWhile if data[0:1]=="\n\n" and not subPacket and self.cmd!="property": #Property packets can get nasty. self.body=Packet(data,True) elif data!=None and len(data) > 0: self.body = data except e: raise DamnParserException,"Unknown error: %s" % e

def __str__(self): with self.__lock: """Returns a dAmn formated packet""" str="" str+=self.cmd if self.param: str+= " %s" % self.param str+="\n" if self.__args != 0: for key,value in self.__args.iteritems(): str+= "%(k)s=%(v)s\n" % {'k':key,'v':value} if self.body: str+="\n" str+=str(self.body) return str

def getArgs(self,data): """Returns a two truple with (dict,string/bool:False)""" #Useful for property packets, eventually add parsing for those types here. data= str(data) #enfore type r={} body=False while True: if len(data) == 0 or data[0] == "\n": break; i = data.find("\n") j = data.find('=') if i==-1 and j!=-1: r[data[0:j]]=data[(j+1):] elif j > i: break; else: r[data[0:j]]=data[(j+1):i] data=data[i:] #EndWhile if data!=None and len(data) > 0: body = data return (r,body)

#Implemented for DictMixin def __getitem__(self,key): #Thread safe return self.__args[key] def __setitem__(self,key,value): self.__args[key]=value def __delitem__(self,key): del self.__args[key] def __contains__(self,key): return key in self.__args def __iter__(): return iter(self.__args) def __len__(): return len(self.__args) def iteritems(): return self.__args.iteritems() def keys(): return self.__args.keys() </python>

Implemented as a dictionary.

  • Parse a Message packet
recv chat:chatroom

msg main
from=username

message

<python>p = Packet("recv chat:chatroom\n\nmsg main\nfrom=username\n\nmessage\0")</python>

    • Access a argument

<python> >>>print p["from"] username </python>

    • Access the command name

<python> >>>print p.cmd recv </python>

    • Access the command parameter

<python> >>>print p.param chat:chatroom </python>

    • Access the message

<python> >>>print p.body.body message </python>

    • Access the type of message (action vs. msg)

<python> >>>print p.body.cmd msg </python>


  • Create a send packet

<python> p = Packet() p.cmd="send" p.param="chat:chatroom" p.body=Packet() p.body.cmd="msg" p.body.param="main" p.body.body="message" </python>

  • Convert for sending to the server

<python> str(p) </python>


This is a much simpler parser written in Python by photofroggy. It has less functionality but parses packets easily.

<python> class Packet:

   """ Use this class to parse dAmn packets.
       Data is stored in the attributes cmd, param,
       args, body and raw.
   """
   def __init__(self, data=None, sep='='):
       self.cmd, self.param, self.args, self.body, self.raw = None, None, {}, None, data
       if not bool(data): return
       if '\n\n' in data:
           self.body = data[data.find('\n\n')+2:]
           data = data[:data.find('\n\n')]
       breaks = data.split('\n')
       if not bool(breaks): return
       if len(breaks) >= 1 and sep not in breaks[0]:
           head = breaks.pop(0).split(' ')
           self.cmd = head[0] or None
           self.param = None if len(head) < 2 else head[1]
       for line in breaks:
           if sep not in line: continue
           self.args[line[:line.find(sep)]] = line[line.find(sep)+len(sep):]
       # And that's the end of that chapter.

</python> The parser could be reduced to a single function and the changes would be minimal. Below is an example of how to use this parser. <python>

  1. Create a packet object with some random data.

pack = Packet("command parameter\narg=foo\narg2=bar\n\nbody") This leaves you with the following: pack.cmd = 'command' pack.param = 'parameter' pack.args = { 'arg' : 'foo', 'arg2': 'bar' } pack.body = 'body' pack.raw = 'command param\narg=foo\narg2=bar\n\nbody' </python>

Ruby

Written by ManjyomeThunder. Released under a GNU General Public License (v3 or above).

class Packet attr_reader :cmd, :param, :sub, :dAmn, :body, :args def initialize(data, dAmn = nil, separator = '=') @args, @body, @dAmn = {}, , dAmn if data.include?("\n\n") @body = data[data.index("\n\n") + 2 .. -1] data = data[0 ... data.index("\n\n")] @sub = Packet.new(@body) unless @body.empty? end data.each_line { |line| if line.include?(separator) @args[line[0 ... line.index(separator)].to_sym] = line[line.index(separator) + separator.length .. -1] else @cmd = (line.include?(' ') ? line[0 ... line.index(' ')] : line).to_sym @param = line[line.index(' ') + 1 .. -1] if line.include?(' ') end } end def [](key) @args[key.to_sym] end def to_s packet = @cmd.to_s packet << ' ' + @param unless @param.nil? packet << "\n" unless packet.empty? @args.each { |key, val| packet << "#{key}=#{val}\n" } packet << "\n#{body}" unless @body.nil? end end

  1. Create a packet object.

packet = Packet.new("recv chat:Botdom\n\nmsg main\nf=ManjyomeThunder\n\nTest message packet", some_dAmn_object)

  1. Access the command and param

puts packet.cmd # prints "recv" puts packet.param # prints "chat:Botdom"

  1. Access packet arguments

puts packet[:f] # prints "ManjyomeThunder"

  1. Access sub-packet and body

puts packet.sub.cmd # prints "msg" puts packet.body # prints "msg main\nf=ManjyomeThunder\n\nTest message packet"

  1. Access dAmn

packet.dAmn.write_packet(packet) # writes the packet back to the server.

Written by nuckchorris0. Released under the MIT License.

Inspired heavily by the dAmnAIR parser.

class Packet

 attr :cmd, :param, :args, :body
 def sub
   unless @body.nil?
     body.split("\n\n").map { |sub| Packet.new sub }
   else
     []
   end
 end
 def raw
   pkt  = "#{@cmd} #{@param}"

   @args.each do |key, val|
     pkt << "\n"
     pkt << "#{key}=#{val}"
   end
   
   unless @body.nil? || @body.empty?
     pkt << "\n"
     pkt << @body
   end
   pkt << "\n"
 end
 def initialize (pkt)
   @args = {}
   @sub = []

   if pkt.is_a? Hash
     @cmd = pkt[:cmd]
     @param = pkt[:param]
     @args = pkt[:args]
     @sub = pkt[:sub]
   elsif pkt.is_a? String
     parts = pkt.split "\n\n", 2
     head = parts.shift.split "\n"

     unless head[0].contains? "="
       cmd = head.shift.split " "
       @cmd = cmd.shift
       @param = cmd.shift
     end

     head.each do |line|
       if line.contains? "="
         splat = line.split "=", 2
         @args[splat[0]] = splat[1]
       end
     end

     @body = parts.shift || ""
   end
 end

end

  1. Generate new Packet object from string (parse)

Packet.new "cmd param\nfoo=bar\n\nbody"

  1. Generate new Packet object from hash

pkt = Packet.new(

 :cmd => "cmd",
 :param => "param",
 :args => { foo: "bar" },
 :body => "body"

)

  1. Convert to raw string

pkt.raw

Java

Not to be confused with JavaScript, which is an unrelated programming language.


Solitude12

This one was written by photofroggy, and ported to Java by Solitude12 (I realize this isn't that amazing, especially since it is not written cleanly. It is a more messy version. I will have to clean it up soon - Solitude12)

<java>import java.util.HashMap; import java.util.ArrayList;

public class Packet { public String cmd, param, body = null; public HashMap<String, String> args = new HashMap<String, String>(); public String raw;

public Packet(String packet){ this(packet, "="); }

public Packet(String packet, String splitter){ this.raw = packet; ArrayList<String> splitted = new ArrayList<String>(); ArrayList<String> splitteddos = new ArrayList<String>(); for (String splits : packet.split("\n\n", -1)){ splitted.add(splits); splitteddos.add(splits); } splitted.remove(0); for(int i = 0; i<splitted.size(); i++){ String bit = splitted.get(i); if(bit.length() > 0) { if (this.body==null) this.body = bit; else this.body = this.body + "\n\n" + bit; } } ArrayList<String> head = new ArrayList<String>(); for (String splits : splitteddos.get(0).split("\n", -1)) head.add(splits); ArrayList<String> splittedtres = new ArrayList<String>(); for (String splits : head.get(0).split(" ", -1)) splittedtres.add(splits); if(splittedtres.size()>0) { this.cmd = splittedtres.get(0); this.param = splittedtres.get(1); head.remove(0); } for(int i = 0; i<head.size(); i++){ String string = head.get(i); int pos = string.indexOf(splitter); if(pos < string.length()-1 && pos > 0) { String[] args2 = string.split(splitter, -1); this.args.put(args2[0], string.substring(pos+1, string.length())); } else if(string.length()>=1) if(i == 0 && this.cmd==null) this.cmd = string; } } }</java>

Using this parser is also easy: <java> // Example of usage: Packet test = new Packet("command parameter\narg=value\narg2=value2\n\nBody"); /* test.cmd -> command test.param -> parameter test.args.get("arg") -> value test.args.get("arg2") -> value2 test.body -> Body

  • /

</java> For the recv packet you can just parse the body again to extract the sub packet.

CombatFrogs

<java>

package dAmn; import java.util.Map; import java.util.HashMap;

/*

* Released under a FreeBSD License, go nuts.
* @author http://combatfrogs.deviantart.com
*/

public class Packet { private String command; private String parameter; private Map<String, String> arguments; private String body;

public Packet(String rawData) { arguments = new HashMap<String, String>();

rawData = replaceTablumps(rawData);

int index = rawData.indexOf("\n\n"); if(index > -1) { body = rawData.substring(index + 2); rawData = rawData.substring(0, index + 1); } else { body = ""; }

String firstLine; if((index = rawData.indexOf('\n')) > -1) { firstLine = rawData.substring(0, index); rawData = rawData.substring(index); } else { firstLine = rawData; rawData = ""; }

if((index = firstLine.indexOf(' ')) > -1) { command = firstLine.substring(0, index); parameter = firstLine.substring(index + 1); } else { command = firstLine; parameter = ""; }

while(!rawData.isEmpty()) { String currentLine; if((index = rawData.indexOf('\n')) > -1) { currentLine = rawData.substring(0, index); rawData = rawData.substring(index + 1); } else { currentLine = rawData; rawData = ""; }

if((index = currentLine.indexOf('=')) > -1) arguments.put(currentLine.substring(0, index), currentLine.substring(index + 1)); } }


public String getCommand() { return command; }

public String getParameter() { return parameter; }

public String getArgument(String key) { if(arguments.containsKey(key)) return arguments.get(key); else return ""; }

public String getBody() { return body; }

private String replaceTablumps(String rawMessage) { String[] original = { "&b\t", "&/b\t", "&i\t", "&/i\t", "&u\t", "&/u\t", "&code\t", "&/code\t", "&sup\t", "&/sup\t", "&sub\t", "&/sub\t", "&br\t", "<", ">", "&emote\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t", "&a\t(.*?)\t\t(.*?)&/a\t", "&link\t(.*?)\t(.*?)\t&\t", "&thumb\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t(.*?)\t", "&avatar\t(.*?)\t(.*?)\t", "&abbr\t(.*?)\t(.*?)&/abbr\t", "&dev\t(.*?)\t(.*?)\t"}; String[] replace = {"", "", "", "", "", "", "", "", "", "", "", "", "\n", "<", ">", "$1", "$1 ($2)", "$1 ($2)", ":thumb$1:", ":icon$1:", "$2", "$1$2"};

for(int i = 0; i < original.length; i++) rawMessage = rawMessage.replaceAll(original[i], replace[i]);


return rawMessage; }


} </java>

PrairieEagle

<java> import java.util.HashMap;

public class Packet { private String command, parameter, body; private HashMap<String, String> args;

public Packet(){ this.command = ""; this.parameter = ""; this.body = ""; this.args = new HashMap<String, String>(); }

public static Packet parse( String data ) throws IllegalArgumentException { if( data == null ) throw new IllegalArgumentException("nothing to parse");

Packet p = new Packet();

int index = data.indexOf("\n\n"); if(index > -1){ p.body = data.substring(index+2); data = data.substring(0, index); }

/* Generally, things get a bit ugly from here */

int le = data.indexOf("="); int ln = data.indexOf("\n");

if(ln != data.length() && le > -1){

String[] lines = data.split("\n"); int count = 0;

if(lines[count].indexOf("=") == -1){ data = lines[count]; count = 1; }

for(; count < lines.length; count++) { String[] arg = lines[count].split("="); p.args.put(arg[0], arg[1]); }

}

if(data == ""){ return p; } // if there is no command or parameter. else { // otherwise int space = data.indexOf(" "); if( space > -1 ){ p.parameter = data.substring(space+1); p.command = data.substring(0, space); }else{ p.command = data; } return p; } }

public String getCommand(){ return this.command; } public String getParam(){ return this.parameter; } public String getBody(){ return this.body; } public String getArg( String key ){ return this.args.get(key); } public String getArgs(){ return this.args.toString(); } public Packet getSubpacket(){ return Packet.parse(this.body); } } </java> Examples of usage: <java> Packet p = Packet.parse("command parameter\narg=value\narg2=value2\n\nBody");

//Then, to get what was parsed: /* p.getCommand() -> packet's command p.getParam() -> packet's parameter p.getArg("arg1") -> the value of 'arg1' p.getBody() -> packet's body

p.getSubpacket() -> packet's subpacket p.getSubpacket().getCommand() ect.

  • /

</java>

JavaScript

miksago

Not to be confused with Java, which is an unrelated programming language.

Experiemental: This is a packet parser developed by miksago, heavily based on Contra's php one: <javascript>

   function parse_pkt(data){
       var packet = {
           cmd: null,
           param: null,
           args: {},
           body: null,
           raw: data
       };
       
       data = data.split("\n\n");
       packet['body'] = data[1] || null;
       
       data = data[0].split("\n");
       
       for(var i=0;i<data.length;i++){
           if(i==0){
               packet['cmd'] = data[i].split(' ')[0];
               packet['param'] = data[i].split(' ')[1] || null;
           } else if(data[i].indexOf("=") != -1){
               packet['args'][data[i].substr(0, data[i].indexOf("="))] = data[i].substr(data[i].indexOf("=")+1);
           }
       }
       return packet;
   }

</javascript>

philo23

Packet parser developed by philo23 for the dAmnAIR library, originally based of miksago's Javascript but then completely rewritten to handle subpackets and pass strict Javascript tests. Note that it's probably not the most efficient parser. Usage is simple PacketParser(raw packet string); No need to pass a depth as its used internally for subpacket parsing. <javascript> function PacketParser (rpkt, depth) { depth = depth || 0; var i; var ppkt = { cmd: null, param: null, args: {}, body: null, sub: [], depth: depth, raw: rpkt };

var parts = rpkt.split("\n\n"); var head = parts.shift().split('\n'); var cmd = head.shift().split(' '); ppkt.cmd = cmd.shift(); ppkt.param = cmd.join(' ');

for (i in head) { if (head.hasOwnProperty(i)) { var val = head[i].split('='); ppkt.args[ val.shift() ] = val.join('='); } } ppkt.body = parts.join('\n\n') || null; if (parts.length >= 1) { i = parts.length - 1; if (i === 1) { ppkt.sub.push( arguments.callee(ppkt.body, depth + 1) ); } else { for (i; i >= 0; i--) { ppkt.sub.push( arguments.callee(parts[i], depth + 1) ); } } }

return ppkt; } </javascript>

Openflock.com Official Parser

Parser

This parser was developed for Openflock.com's webchat by plaguethenet. It is based on the ideas in the official dAmn Parser, but is clean code. Notably, it doesn't use a regular expression to handle args. This parser also will not break like the other two, it will correctly parse any valid packet. Subpackets are handled by either using parse (or parseArgs in the case of the login body, join body, etc.) <javascript> function parse(data) {

   if(!data) {
       return null;
   }
   try {
       var cmd;
       var param;
       var idx = data.search('\n');
       if(idx == 0) {
           return null;
       }
       var headerline = data.substr(0, idx).replace(/\s*$/, );
       cmd = headerline.match(/\S+/)[0];
       var sidx = headerline.search(' ');
       if(sidx && sidx > 0)
           param = headerline.substr(sidx+1).match(/\S.*/)[0].replace(/\s*$/, );
       var args = parseArgs(data.substr(idx + 1));
       return {
           'cmd' : cmd,
           'param' : param,
           'arg' : args.args,
           'body' : args.data
       };
   } catch(e) {
       alert('parser exception:' + e);
       return null;
   }

}

function parseArgs(data) {

   var args = new Object();
   var body = ;
   var work = data;
   while(work && work.search('\n')) {
       var i = work.search('\n');
       var tmp = work.substr(0, i);
       work = work.substr(i + 1);
       i = tmp.search('=');
       if(i == null || i <= 0) {
           throw "bad argument line:" + tmp;
       }
       an = tmp.substr(0, i)
       av = tmp.substr(i + 1)
       args[an.replace(/\s*$/, )] = av.replace(/\s*$/, );
   }
   if(work) {
       body = work.substr(1);
   }
   return {
       'args' : args,
       'data' : body
   };

}</javascript>

Packet Creator

This is a simple function that will create sendable packets. The constructed packets are valid for both dAmn and OpenFlock chat.

<javascript> function makepacket(cmd, param, args, body) {

   var ret = cmd;
   if(param) {
       ret = ret + " " + param;
   }
   ret = ret + "\n";
   if(args) {
       for(var key in args) {
           ret = ret + key + "=" + args[key] + "\n";
       }
   }
   ret = ret + "\n";
   if(body) {
       ret = ret + body;
   }
   return ret;

}

//Small helper to turn a packet back into a string. function serialze(pkt) {

   return makepacket(pkt.cmd, pkt.param, pkt.arg, pkt.body)

}</javascript>

Using the parser and creator works like this: <javascript> pkt = parse('recv chat:Botdom\n\nmsg main\nfrom=plaguethenet\n\nHallo!') pkt2 = parse(serialize(pkt)) //both of these objects are identical regarding the contents. alert(JSON.stringify(pkt) + "\n-----------\n\n" + JSON.stringify(pkt2)) //If you need the parsed body, You'd do something along these lines: pkt.sub = parse(pkt.body) //msg alert(pkt.sub.cmd) //plaguethenet alert(pkt.sub.arg.from) //Hallo! alert(pkt.sub.body) </javascript>

Haskell

This one was made by deviant-garde. <haskell> import Data.List import Data.ByteString.Lazy.Char8 (ByteString) import qualified Data.ByteString.Lazy.Char8 as L

makePacket :: ByteString -- ^ The packet to convert.

          -> Packet -- ^ The packet converted into a Packet datatype.

makePacket = makePacketSep '='

-- | Convert a packet in string form into a 'Packet', specifying -- the argument seperator. makePacketSep :: Char -- ^ The argument seperator.

             -> ByteString -- ^ The packet to convert.
             -> Packet     -- ^ The packet converted into a Packet datatype.

makePacketSep sep str =

   let body = case subIndex "\n\n" str of
                   Nothing -> ""
                   Just n  -> L.drop (fromIntegral n+2) str
       lines' = L.lines str
       (cmd,param) = break' ' ' $ if hasHeader then head lines' else L.empty
       args = map (break' sep) . (if hasHeader then drop 1 else id)
            $ takeWhile (not . L.null) lines'
       break' delim = fmap (L.drop 1) . L.break (delim==)
       hasHeader = not (null lines') && not (L.elem sep (head lines'))
       subIndex substr str = findIndex (L.isPrefixOf substr) (L.tails str)
   in Packet cmd param (M.fromList args) body str

</haskell>

One notable feature of this parser is that it can parse packets without cmds or params in them.

Lua

This one was authored by incluye. <lua> -- requires LPeg regex package local re = require("re")

local Packet = {}

-- don't worry this isn't actually a regex Packet.pattern = re.compile[[ text <- packet -> {} packet <- cmd param? %nl? arglist? nest? nest <- (%nl {:subpacket: text :}) / body cmd <- {:cmd: [a-zA-Z]+ :} param <- ' ' {:param: [a-zA-Z0-9:\.]+ :} arglist <- {:args: ((argpair -> {} %nl) %nl? (argpair -> {} %nl)*) -> {} :} argpair <- {:key: [^%nl=]+ :} '=' {:value: [^%nl]+ :} body <- {:body: (.)+ :} ]]

function Packet.__index (self, key) return self.args[key] or Packet[key] end

function Packet.new (str) local match if type(str) == "table" then match = str else match = Packet.pattern:match(str) end local o = {} o.cmd = match.cmd o.param = match.param o.body = match.body o.args = {} if match.args then for _, v in ipairs(match.args) do o.args[v.key] = v.value end end if match.subpacket then o.subpacket = Packet.new(match.subpacket) end setmetatable(o, Packet) return o end

return Packet </lua>

Called like <lua>local packet = require("packet") local mypacket = packet.new(str)

-- mypacket.cmd -- mypacket.param -- mypacket.args


mypacket.args.somekey
mypacket.args.someotherkey
OR
mypacket["somekey"]
mypacket.somekey

-- mypacket.subpacket -- mypacket.body</lua>

Notice: this parser parses subpackets recursively, so you'll get a fairly deep list if you parse, say, a whois property packet.

C

incluye

This one was also written by incluye.

<c>

  1. include <stdlib.h>
  2. include <stdio.h>
  3. include <string.h>

typedef enum {

   false = 0,
   true = 1

} bool;

struct _arglist {

   char *key;
   char *value;
   struct _arglist *next;

};

typedef struct _arglist arglist;

arglist *al_make(void); void al_set(arglist*, char*, char*); char *al_get(arglist*, char*); char *al_get_def(arglist*, char*, char*); void al_free(arglist*); void al_print(arglist*);

struct _packet {

   char *command;
   char *subcommand;
   arglist *args;
   char *body;

};

typedef struct _packet packet;

packet *parse(char*, bool); size_t _parse_cmd(packet*, char*); size_t _parse_subcmd(packet*, char*); size_t _parse_body(packet*, char*); size_t _parse_largpair(packet*, char*); size_t _parse_argpair(packet*, char*); void packet_free(packet*); void packet_print(packet*);

  1. define subpacket(p) parse(p->body, false)

// associative array functions

arglist *al_make() {

   arglist *a = malloc(sizeof(arglist));
   return a;

}

void al_set(arglist *a, char *key, char *value) {

   arglist *cur = a;
   if (a->key == NULL) {
       a->key = key;
       a->value = value;
       return;
   }
   
   while (cur->next != NULL) cur = cur->next;
   cur->next = al_make();
   cur->next->key = key;
   cur->next->value = value;

}

char *al_get(arglist *a, char *key) {

   arglist *cur = a;
   do {
       if (strcmp(cur->key, key) == 0)
           return cur->value;
   } while ((cur = cur->next) != NULL);
   return NULL;

}

char *al_get_def(arglist *a, char *key, char *default_val) {

   char *res = al_get(a, key);
   return res == NULL ? default_val : res;

}

void al_free(arglist *a) {

   if (a->next != NULL)
       al_free(a->next);
   free(a->key);
   free(a->value);
   free(a);

}

void al_print(arglist *a) {

   arglist *cur = a;
   printf("args: {");
   while (cur != NULL) {
       printf("\n   %s: %s", cur->key, cur->value);
       cur = cur->next;
       if (cur == NULL)
           printf("\n");
   }
   printf("}\n");

}

// packet parsing size_t _parse_cmd(packet *p, char *str) {

   p->command = malloc(sizeof(char*));
   size_t idx = 0;
   do { idx++; } while (str[idx] != ' ' && str[idx] != '\n');
   idx++;
   p->command = malloc(idx);
   strncpy(p->command, str, idx - 1);
   p->command[idx] = '\0';
   return idx - 1;

}

size_t _parse_subcmd(packet *p, char *str) {

   if (*str != ' ')
       return 0;
   size_t idx = 0;
   str++;
   do { idx++; } while (str[idx] != '\n');
   idx++;
   p->subcommand = malloc(idx);
   strncpy(p->subcommand, str, idx - 1);
   p->subcommand[idx] = '\0';
   return idx + 1;

}

size_t _parse_body(packet *p, char *str) {

   p->body = str;
   return 0; // doesn't matter, because nothing is called after this

}

size_t _parse_argpair(packet *p, char *str) {

   // at the end of the argument pairs
   if (*str == '\0' || *str == '\n') return 0;
   
   if (p->args == NULL) 
       p->args = al_make();
   
   size_t idx = 0, idx_n = 0;
   
   // get the key
   do { idx++; } while (str[idx] != '=' && str[idx] != '\n');
   if (str[idx] == '\n') return idx + 1;
   idx++;
   char *key = malloc(idx);
   strncpy(key, str, idx - 1);
   key[idx] = '\0';
   str += idx;
   
   // get the value!
   do { idx_n++; } while (str[idx_n] != '\n');
   idx_n++;
   char *value = malloc(idx_n);
   strncpy(value, str, idx_n - 1);
   value[idx_n] = '\0';
   
   // make the argument pair
   al_set(p->args, key, value);
   
   // consumed key, value, '=' and '\n'
   return idx + idx_n;

}

packet *parse(char *str, bool skip_newline) {

   packet *p = malloc(sizeof(packet));
   str += _parse_cmd(p, str);
   str += _parse_subcmd(p, str);
   
   if (*str != '\n' && *str != '\0') {
       size_t s;
       if (skip_newline) {
           s = _parse_argpair(p, str);
           str += (s + 1); // eat the extra newline
       }
       while ((s = _parse_argpair(p, str)) > 0) {
           str += s;
       }
   }
   
   if (*str == '\n')
       _parse_body(p, ++str);
   
   return p;

}

void packet_free(packet *p) {

   if (p->args != NULL)
       al_free(p->args);
   free(p->command);
   free(p->subcommand);
   free(p);

}

void packet_print(packet *p) {

   if (p->command != NULL)
       printf("command = %s\n", p->command);
   
   if (p->subcommand != NULL)
       printf("subcommand = %s\n", p->subcommand);
   
   al_print(p->args);
   
   if (p->body != NULL && strlen(p->body) > 0)
       printf("body = %s\n", p->body);

}

</c>

<c> packet *p1 = parse(some_damn_packet, false); packet *p2 = parse(login_packet, true); // skips the extra newline in between arguments packet_print(p1); printf("=========\n"); packet_print(p2); </c>


PrairieEagle

This is a packet parser by PrairieEagle that was made after taking inspiration from how the packet parser for C# by plaguethenet is setup.

<c>

  1. include <string.h>
  2. include <stdio.h>
  3. include <stdlib.h>

typedef struct { char *key, *value; } Dictionary;

typedef struct { Dictionary *args; int argc; char *body; } dAmnArgs;

typedef struct { char *cmd, *param, *raw; dAmnArgs *argsAndBody; } dAmnPacket;

/// Get Arguments and Body /// /// this parses the data into dAmnArgs *getArgsNData( char *data ) {

   dAmnArgs *p = (dAmnArgs*) malloc (sizeof(dAmnArgs));
   p->argc = 0;
   p->args = (Dictionary*) malloc (sizeof(Dictionary));
   p->body = NULL;
   while (1)
   {
       if (strlen(data) == 0 || data[0] == '\n' ) break; // skip if it has no args
       char *eq_sign = strchr(data, '=');
       char *n_line = strchr(data, '\n');
       if (eq_sign != NULL && eq_sign < n_line )
       {
           //size_t idx = eq_sign-data; // find the equal sign
           p->args[p->argc].key = (char*) malloc (eq_sign-data);
           strncpy (p->args[p->argc].key, data, (eq_sign-data)-1 );
           p->args[p->argc].key[eq_sign-data] = '\0';
           data += (eq_sign-data)+1;  // Skip the equal sign, move the data pointer forward.
           p->args[p->argc].value = (char*) malloc (n_line-data);
           strncpy (p->args[p->argc].value, data, (n_line-data)-1);
           p->args[p->argc].value[n_line-data] = '\0';
           data += (n_line-data)+1;
           p->argc++; // we now have '1' argument in the packet
       }


   }
   if (strlen(data) > 0)
   {
       p->body = (char*) malloc (strlen(data) - 1);
       strcpy (p->body, data+1);
   }
   return p;

}

/// Packet Parser /// main packet parser dAmnPacket *parsePacket( char *data ) {

   dAmnPacket *p = (dAmnPacket*) malloc (sizeof(dAmnPacket));
   p->raw = (char*) malloc (strlen(data)+1);
   strcpy(p->raw, data);
   p->cmd = NULL;
   p->param = NULL;
   size_t fnl = strchr(data, '\n') - data; // first new line.
   size_t fs = strchr(data, ' ') - data; // first space that seperates command and parameter
   if (fnl != NULL) // not a valid packet without a line break in it.
   {
       if (fs != NULL && fs < fnl) // a parameter exists
       {
           p->cmd = (char*) malloc (fs);
           strncpy (p->cmd, data, fs);
           p->cmd[fs] = '\0';
           p->param = (char*) malloc (fnl - fs + 1);
           strncpy (p->param, data+(fs+1), (fnl - fs));
           p->param[fnl - fs + 1] = '\0';
       }else{
           p->cmd = (char*) malloc (fnl);
           strncpy (p->cmd, data, fnl);
           p->cmd[fnl] = '\0';
       }
       data += fnl+1; // done with the first line, set pointer past it.
       if (strlen(data) == 0)
           p->argsAndBody = getArgsNData( "\n\n" );
       else
           p->argsAndBody = getArgsNData( data );
   }
   return p;

}

char *getValueInArgs( dAmnPacket *p, char *key ) {

   int index;
   for(index = 0; index <= p->argsAndBody->argc; index++){
       if (strcmp(key, p->argsAndBody->args[index].key) == 0)
           return p->argsAndBody->args[index].value;
   }
   return (char*)NULL;

}


/// Deallocate packet /// use th is when you no longer have a need for a packet. void destroyPacket( dAmnPacket *p ){

   if (p->cmd != NULL) free(p->cmd);
   if (p->param != NULL) free(p->param);
   while (p->argsAndBody->argc > 0)
   {
       free( p->argsAndBody->args[p->argsAndBody->argc-1].key );
       free( p->argsAndBody->args[p->argsAndBody->argc-1].value );
       p->argsAndBody->argc -= 1;
   }
   free( p->argsAndBody->args );
   free( p->argsAndBody );
   if (p->argsAndBody->body != NULL) free(p->argsAndBody->body);
   free( p );

} </c>

This works very similar to the packet parser by plaguethenet. it is, in fact, setup almost exactly the same way: <c> dAmnPacket *p = parsePacket( data ); </c>

Perl

This is a parser done in Perl by DivinityArcane. It's used in dAmnPerl.

<perl># Perl dAmnPacket parser - Part of dAmnPearl

  1. Author: Justin Eittreim <eittreim.justin@live.com>
  2. Date: Wed Oct 31 2012 23:59

package dAmnPacket;

   sub parse {
       my $packet = $_[0];
       my %self = ();
       
       $self{command} = ;
       $self{parameter} = ;
       $self{subCommand} = ;
       $self{subParameter} = ;
       $self{body} = ;
       $self{raw} = ;
       $self{arguments} = ();
           
       $packet =~ s/\0//;
       
       ($self{raw} = $packet) =~ s/\n/\\n/;
       
       my $nl_pos = index($packet, "\n");
       my $chunk = substr $packet, 0, $nl_pos;
       
       if ((my $space_pos = index($packet, ' ')) != -1) {
           $self{command} = substr $chunk, 0, $space_pos;
           $self{parameter} = substr $chunk, $space_pos + 1;
       } else {
           $self{command} = $chunk;
       }
       
       $packet = substr $packet, $nl_pos + 1;
           
       if ((my $nlnl_pos = rindex($packet, "\n\n")) != -1) {
           $self{body} = substr $packet, $nlnl_pos + 2;
           $packet = substr $packet, 0, $nlnl_pos;
       }
       
       my @chunks = split /\n/, $packet;
       foreach my $piece (@chunks) {
           if (length $piece > 0) {
               if ((my $sep_pos = index($piece, '=')) != -1) {
                   $self{arguments}{substr $piece, 0, $sep_pos} = substr $piece, $sep_pos + 1;
               } else {
                   if (($space_pos = index($packet, ' ')) != -1) {
                       $self{subCommand} = substr $piece, 0, $space_pos - 1;
                       $self{subParameter} = substr $piece, $space_pos;
                   } else {
                       $self{subCommand} = $piece;
                   }
               }
           }
       }
       
       return %self;
   }
   

1;</perl>


Go

This is a simple parser done in Go by DivinityArcane.

<c>type DAmnPacket struct {

   Command, Parameter, SubCommand, SubParameter, Body string
   Args map [string] string

}

func (p *DAmnPacket) Parse (pkt string) bool {

   p.Args = make(map [string] string)
   pos := 0
   
   if pos = strings.Index(pkt, "\n"); pos == -1 {
       return false
   }
   header := pkt[0:pos]
   
   if spos := strings.Index(header, " "); spos != -1 {
       p.Command   = header[0:spos]
       p.Parameter = header[spos+1:]
   } else {
       p.Command   = header
   }
   pkt = pkt[pos+1:]
   if pos = strings.Index(pkt, "\n\n"); pos != -1 {
       p.Body = pkt[pos+2:]
       pkt    = pkt[0:pos]
   }
   data := strings.Split(pkt, "\n")
   for _, chunk := range data {
       if len(chunk) == 0 {
           continue
       }
       if pos = strings.Index(chunk, "="); pos != -1 {
           key, value := chunk[0:pos], chunk[pos+1:]
           p.Args[key] = value
           continue
       }
       if pos = strings.Index(chunk, " "); pos != -1 {
           p.SubCommand, p.SubParameter = chunk[0:pos], chunk[pos+1:]
       }
   }
   return true

}

func (p *DAmnPacket) PullBodyArgs () bool {

   if len(p.Body) <= 0 {
       return false
   }
   if !strings.Contains(p.Body, "\n") || !strings.Contains(p.Body, "=") {
       return false
   }
   data := strings.Split(p.Body, "\n")
   
   for _, chunk := range data {
       if len(chunk) == 0 {
           continue
       }
       if pos := strings.Index(chunk, "="); pos != -1 {
           key, value := chunk[0:pos], chunk[pos+1:]
           p.Args[key] = value
           continue
       }
   }
   return true

}</c>


Example usage:

<c>packet  := new(DAmnPacket) packet.Parse(string(buffer))

fmt.Printf("Packet type: %s\n", packet.Command)</c>

Objective-C

Below is a packet object for Objective-C, created by photofroggy.

<c> // // PFYdAmnPacket.m // PFYChatMilk // // Created by Henry on 05/05/2014. // Copyright (c) 2014 dAmnLab. All rights reserved. //

  1. import "PFYdAmnPacket.h"

@implementation PFYdAmnPacket

+ (id) createFromString:(NSString *)pkt {

   return [[PFYdAmnPacket alloc] initWithString:pkt];

}

+ (id) createFromNSData:(NSData *)pkt {

   return [[PFYdAmnPacket alloc] initWithNSData:pkt];

}

- (id) parse: (NSString *) pkt {

   // Remove the trailing \0
   self.raw = [pkt componentsSeparatedByString:@"\0"][0];
   
   // Separate header from body
   NSArray *buf = [self.raw componentsSeparatedByString:@"\n\n"];
   NSString *head = buf[0];
   buf = [buf subarrayWithRange:NSMakeRange(1, [buf count] - 1)];
   self.body = [buf componentsJoinedByString:@"\n\n"];
   
   // Split the header
   NSArray *hbuf = [head componentsSeparatedByString:@"\n"];
   NSArray *clbuf = [hbuf[0] componentsSeparatedByString:@" "];
   hbuf = [hbuf subarrayWithRange:NSMakeRange(1, [hbuf count] - 1)];
   NSUInteger cllen = [clbuf count];
   
   // Command
   if(cllen > 0)
       self.cmd = clbuf[0];
   // Param
   if(cllen > 1)
       self.param = [[clbuf subarrayWithRange:NSMakeRange(1, cllen - 1)] componentsJoinedByString:@" "];
   
   // Arguments
   NSRange sepr;
   for(NSString *argline in hbuf) {
       sepr = [argline rangeOfString:@"="];
       if(sepr.location == NSNotFound)
           continue;
       [self.arguments
        setValue:[argline substringFromIndex:sepr.location + sepr.length]
        forKey:[argline substringToIndex:sepr.location]
        ];
   }
   
   return self;

}

- (id) argument:(NSString *)key {

   return [self.arguments objectForKey:key];

}

- (NSString *) stringValue {

   NSString *pkt;
   
   if([self.cmd length] > 0) {
       pkt = self.cmd;
       if([self.param length] > 0) {
           pkt = [pkt stringByAppendingFormat:@" %@", self.param];
       }
       pkt = [pkt stringByAppendingString:@"\n"];
   }
   
   for(NSString *key in [self.arguments keyEnumerator]) {
       pkt = [pkt stringByAppendingFormat:@"%@=%@\n",
              key, [self.arguments objectForKey:key]];
   }
   
   if(self.sub != nil) {
       pkt = [pkt stringByAppendingFormat:@"\n%@",
              [self.sub stringValue]];
   } else if([self.body length] > 0){
       pkt = [pkt stringByAppendingFormat:@"\n%@", self.body];
   }
   
   return pkt;

}

@end </c>

Usage is fairly simple.

<c> PFYdAmnPacket *pkt = [PFYdAmnPacket createFromString:@"recv chat:Botdom\n\nmsg main\nfrom=username\n\nHello, World!\0"]; pkt.sub = [PFYdAmnPacket createFromString:pkt.body];

// pkt.cmd = "recv" // pkt.param = "chat:Botdom" // pkt.sub.cmd = "msg" // pkt.sub.param = "main" // [pkt.sub argument:@"from"] = "username" // pkt.sub.body = "Hello, World!" </c>

The object can also be used to create packets.

<c> PFYdAmnPacket *pkt = [PFYdAmnPacket new];

pkt.cmd = @"send"; pkt.param = @"chat:Botdom"; pkt.sub = [PFYdAmnPacket new]; pkt.sub.cmd = @"msg"; pkt.sub.param = @"main"; pkt.sub.body = @"Hello, World!";

NSString *pstr = [pkt stringValue];

// pstr = "send chat:Botdom\n\nmsg main\n\nHello, World!" </c>